Skip to content

survey: C# unit test strategy for core use cases #51

Description

@aoirint

Warning

This issue was created with assistance from LLMs.

Summary

  • Research and define an initial C# automated testing strategy for this repository.
  • Start with fast unit tests around the existing Core layer and defer Unity/BepInEx runtime checks to manual or later integration testing.
  • Add the test command to CI after the first focused test project is in place.

Current Repository Findings

  • The repository currently has one production project, CruiserJumpPractice/CruiserJumpPractice.csproj, targeting netstandard2.1 with explicit LangVersion 13.0.
  • CI currently runs restore, dotnet format, build, and Markdown lint, but no C# test job.
  • The current architecture is already test-friendly in CruiserJumpPractice/Core:
    • use cases depend on IGameInterop, IPracticeInput, ICoreLogger, and CruiserStateStore rather than direct Unity APIs;
    • result enums are small and behavior-oriented;
    • CruiserSnapshot and Vector3Value are plain data types.
  • Most candidate test subjects are internal, so a test project will need either InternalsVisibleTo or a deliberate public API/testing boundary decision.

Research Notes

Proposed Approach

  • Add a separate test project, for example CruiserJumpPractice.Tests/CruiserJumpPractice.Tests.csproj.
  • Prefer targeting a modern runtime such as net10.0 for the test project while referencing the production netstandard2.1 project, so tests align with the repository's documented SDK/CI baseline without changing mod runtime compatibility.
  • Use xUnit.net v3 or another explicit framework chosen in the implementation PR. xUnit.net v3 is a good default candidate because it is modern, widely supported, and compatible with current .NET testing guidance, but the implementation PR should confirm package versions and runner behavior before committing.
  • Add focused hand-written fakes for IGameInterop, IPracticeInput, and ICoreLogger before introducing a mocking library. The interfaces are small enough that fakes should keep tests readable and dependency-light.
  • Keep tests behavior-focused with Arrange/Act/Assert naming such as Execute_WhenCallerIsNotHost_ReturnsHostOnly.
  • Avoid Unity, BepInEx, Netcode, filesystem, Steam, and game-process dependencies in the first unit test layer.
  • Keep integration or smoke testing for game interop as a separate future effort, likely requiring manual game-version matrices or a dedicated harness.

Initial Test Coverage Candidates

  • RequestSaveCruiserStateUseCase
    • non-host shows host-only tip and does not request server save;
    • host requests save and returns success.
  • RequestLoadCruiserStateUseCase
    • non-host shows host-only tip and does not request server load;
    • host requests load and returns success.
  • ToggleMagnetUseCase
    • non-host shows host-only tip and does not toggle;
    • host toggles from off to on and reports MagnetOn;
    • host toggles from on to off and reports MagnetOff.
  • SaveCruiserStateUseCase
    • no cruiser returns NoCruiserFound and does not overwrite state;
    • captured cruiser state is stored and returns Success;
    • thrown interop exception returns UnexpectedState and logs an error.
  • LoadCruiserStateUseCase
    • no cruiser returns NoCruiserFound;
    • missing saved state returns NoSavedState;
    • magneted cruiser returns MagnetedToShip and does not restore;
    • valid saved state restores and returns Success;
    • thrown interop exception returns UnexpectedState and logs an error.
  • FrameHandler
    • busy local player skips all inputs;
    • save/load/toggle inputs dispatch the matching use cases only once per frame.
  • Presentation use cases
    • each result enum value maps to the expected tip or log behavior.

Acceptance Criteria

  • Decide the initial framework and runner strategy, documenting why it fits this repository.
  • Add a separate C# test project to the solution without changing the production target framework.
  • Add only the minimum test visibility change needed, such as InternalsVisibleTo("CruiserJumpPractice.Tests"), if the implementation keeps production types internal.
  • Add focused unit tests for the highest-value Core use cases before testing Unity-facing adapters.
  • Add dotnet test --no-restore or equivalent to CI after restore and before packaging/release work.
  • Update README/CONTRIBUTING verification commands to mention the test command.
  • Keep NuGet lock-file behavior deterministic and update any new lock files intentionally.

Verification Performed During Research

  • Confirmed current repository has no C# test project.
  • Confirmed dotnet --version reports 10.0.201 locally.
  • Confirmed current build succeeds with dotnet build --no-restore.
  • Searched existing issues for testing-related work and did not find a duplicate automated-test setup issue.

Non-Goals

  • Do not require launching Lethal Company in the initial unit test suite.
  • Do not test Unity/BepInEx adapters through reflection-heavy or game-process-dependent tests in the first step.
  • Do not introduce coverage thresholds until the suite has meaningful behavioral coverage.
  • Do not change mod runtime compatibility away from netstandard2.1 as part of test setup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions