Skip to main content
SET has one property that makes testing unusually important: the rules are perfectly deterministic. Given the same deck seed and the same sequence of player inputs, every mode — Single Player, Pass & Play, and Online Multiplayer — must produce the exact same outcome. A bug in SetValidator or a missed state transition in GameSession does not corrupt one mode; it corrupts all three simultaneously. This is why Domain and Application code must be tested to 100% coverage before any UI or network work builds on top of it.
All unit tests must pass in CI before a PR can merge. A single failing test blocks the entire team. Never push a test in a commented-out or [Ignore]-attributed state without a linked ticket and a deadline for re-enabling it.
The diagram below shows the testing strategy across layers, the tools used at each level, and the coverage targets. EditMode tests are fast and run on every push; PlayMode tests are reserved for integration concerns that genuinely require a running Unity scene.

Test Categories

EditMode tests run without a Unity process (no MonoBehaviour lifecycle). They execute fast — a full Domain + Application suite should complete in under 5 seconds. PlayMode tests spin up a Unity scene and are slower; reserve them for integration concerns that genuinely require a running scene.

Writing Unit Tests

Basic SetValidator Test

This is the simplest form: no mocks needed because SetValidator is a pure function.

Testing GameSession with NSubstitute Mocks

When testing GameSession, inject mocked dependencies so your test controls exactly what ISetValidator and IAIScanner return.

Naming Convention

Every test method follows the three-part pattern:
Good names:
  • Validate_AllDifferent_ReturnsValid
  • Validate_MixedColor_ReturnsInvalidWithReason
  • ClaimSelected_InvalidSet_PointPenaltyApplied
  • AnySetExists_BoardWithNoValidSet_ReturnsFalse
  • HandleCommand_InWaitingForServerState_DiscardsClaim
Bad names:
  • TestValidation — what is being validated? what result?
  • Test1 — meaningless
  • WorksCorrectly — every test should work correctly; this adds no information

AAA Pattern

Every test body must have three clearly separated sections. Use a blank line between each.
Never mix act and assert into a single statement like Assert.IsTrue(validator.FindAllSets(board).Count > 0) — it obscures the intent and makes failures harder to diagnose.

Coverage Targets

Coverage percentage is a floor, not a goal. 100% branch coverage on SetValidator with weak assertions is worthless. Write tests that would catch a real bug — wrong attribute check, off-by-one board expansion, missed penalty deduction.

What to Test

SetValidator — Cover Every Combination

Board — Invariants

GameSession — State Machine

AI (deterministic with fixed seed)


What NOT to Test

  • Auto-properties with no logic. Testing that card.Color == Color.Red after card = new Card(Color.Red) proves nothing.
  • Visual appearance. Animation curves, particle counts, and shader parameters cannot be meaningfully asserted in code.
  • Animation timing. If an animation takes 0.3 s, don’t write a test for “0.3 seconds elapsed.” That couples your test suite to an art decision.
  • Unity internals. You don’t need to verify that Text.text = "5" makes text appear on screen. Trust the engine.

Injecting Time for Deterministic Tests

GameSession timed mode relies on elapsed time. Never use Time.deltaTime directly in Domain or Application code — that is a Unity API. Instead, inject an ITimeProvider interface.

CI Integration

Every push to any branch and every PR triggers the full unit test suite. The pipeline fails — and the PR is blocked — if:
  • Any EditMode test fails
  • Any PlayMode test fails
  • dotnet format reports style violations
  • Any Roslyn analyzer rule fires at error severity
Do not rely on “it worked locally.” Always verify with git push and watch the pipeline.

Common Mistakes


Coding Conventions

Naming and formatting rules that apply equally to test files.

Approved Patterns

Constructor injection makes mock substitution straightforward.

PR Checklist

The test-related gates every PR must pass before merge.

Phase Breakdown

Phase 2 and 3 DoDs that set the coverage baseline for the whole project.