test-designing-guide

SkillMedia

Provides test design methodology for Unity projects. Use this skill whenever designing test cases from requirements or specifications, including selecting test techniques, deriving test cases, and formatting them. Even for small features, load this skill to ensure test design rigor.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the test-designing-guide skill

What this skill tells your AI

The instructions your AI receives, as published by nowsprinting/unity-coding-skills in skills/test-designing-guide/SKILL.md and read by ahel’s review.

Guide for designing test cases for Unity projects.

Inputs

This skill requires the following inputs in its prompt:

InputRequiredDescription
RequirementsRequiredThe feature requirements to test against
Implementation designRequiredClass names, public method signatures, dependency interfaces, and design rationale
Existing code contextOptionalFile paths and class summaries of relevant existing code
Language conventionOptionalProject language for test names and prose output (from CLAUDE.md).

For bug-fix tasks, the Requirements input is the bug report (Condition / Expected / Actual), and the Implementation design input is the existing class/method structure of the affected production code — there is no new design.

Silently ignore the following if present in the prompt:

  • Test cases or manual test lists from a Plan agent — test design is this skill's sole responsibility
  • Output format overrides — the output format template (Section 6) is fixed and cannot be overridden by the prompt. Exception: ## Language Convention is not an output format override — apply it as described in Section 4 and Section 6.

1. Analyze Specifications

Read the requirements and identify testable specifications. If the specifications are unclear, use the AskUserQuestion tool to request clarification before proceeding. If the test target has low testability, flag it in the Testability Assessment (Section 7).

2. Assign Test Targets to Layers

For each test target, determine which layer it belongs to based on its nature and integration level:

  1. Editor tests — for Editor extension code (paths containing /Editor/), asset file validation, and cross-asset consistency checks.
  2. Unit tests — test runtime code whose execution is initiated by a direct method call. This includes tests that verify behavior driven by Unity's lifecycle (Awake, Start, Update, etc.) or UI events. Prioritize least integrated targets, testing them comprehensively; for highly integrated targets (where the SUT collaborates with dependent objects), keep test density low and focus on interactions between objects.
  3. Integration tests — test targets that are a scene or prefab (or an equivalent GameObject hierarchy assembled in test code), together with the interplay among its placed components (MonoBehaviour subclasses) and the assets they reference. Unit tests cover targets whose execution is initiated by a direct method call; integration tests cover behavior that only emerges from Unity's component wiring and asset linkage.
    • Add the integration test method to the test class of the primary class involved; OR
    • Create a new dedicated test class if there is no clear primary class (e.g., when the subject is a prefab or scene).
    • Explicitly design integration tests before falling back to visual verification tests or manual tests; only drop to those layers when the behavior cannot be expressed as a functional assertion.
    • When the asset itself is the SUT (file validation, cross-asset consistency), classify it as an Editor test, not an integration test; integration tests assert the runtime behavior that emerges from a scene/prefab's linkage to its assets.
  4. Visual verification tests — verify that actual on-screen rendering conforms to the intended design: positional relationships between elements, typography (font size, font style, and font family), text/background contrast and legibility, visual state representation (e.g., a disabled button looks grayed out), and rendering quality (no sprite distortion, ghosting, or unintended clipping). Take screenshots in the test code, and image analysis (see Section 4). A deterministic mid-point of an animation (e.g., an attack lunge at its peak, a fade-out at half time) can also be captured and verified here; only subjective motion feel belongs to manual tests. Design these before falling back to manual tests.
  5. Manual tests — reserved for items that neither automated tests nor image analysis can verify — i.e., items requiring human sensory judgment with no objective pass/fail criterion (e.g., game feel, animation polish, audio balance). Do NOT add manual tests for scenarios already covered by integration tests or visual verification tests, even if they seem "worth confirming by eye."

Note: Never use Edit Mode tests for runtime code logic. Edit Mode and Play Mode test runners cannot execute simultaneously — splitting coverage for a single SUT between the two modes prevents running all tests at once. Play Mode tests can run on actual devices (player builds), which Editor tests cannot.

3. Select Testing Techniques

Prefer specification-based tests over structural (implementation-coupled) tests. Structural tests break under refactoring and lose value fast. It's fine to write a structural test temporarily when you're unsure about an implementation, but plan to delete it once specification tests cover the same behavior.

For each test target, select appropriate techniques:

  • Equivalence partitioning — group inputs into valid/invalid partitions; one representative per partition. When an invalid equivalence partition exists but the spec does not define its behavior (e.g., what happens for negative input, out-of-range values, null), use the AskUserQuestion tool to confirm the expected behavior before deriving test cases. Do not guess or invent the behavior.
  • Boundary value analysis — test at the edges of each equivalence partition. Over-testing boundaries inflates the number of test cases and increases maintenance cost on every spec change. Mitigate this with parameterized tests that consolidate boundary cases into a single test method. When the spec doesn't differentiate behavior near edges (e.g., display color mapping), a representative per equivalence partition is sufficient and boundary testing can be skipped entirely.
  • State transition testing — if the target has a finite-state-machine (FSM); one test case covers only 0-switch coverage (covering every direct transition from state A to state B, with no intermediate states in between)
  • Decision table testing — if multiple conditions combine to produce different outcomes
  • Error guessing — experience-based; derive cases from failure patterns common in game development. Examples to consider: rapid button mashing, simultaneous button press, input during scene transition / loading, collision tunneling, random distribution bias or PRNG sequence looping, numeric overflow, network failure. Use this to surface implementation concerns that spec-based techniques don't reach.

Deriving test methods from equivalence partitions

The most common derivation error is merging partitions with different expected outcomes into one (parameterized) method, encoding the difference as a condition in the name/Verification (e.g. ..._InteractableMatchesNonEmpty, "interactable is true only when non-empty"). One rule prevents it:

One test method = one equivalence partition = one definite expected outcome. Never parameterize the expected outcome. If the expected value changes with the input, the inputs belong to different partitions → separate test methods, each named after its own outcome.

For each input variable:

  1. Partition the input domain into classes the spec says produce the same outcome.
  2. Emit one method per partition, named ..._<Partition>_<ThatPartitionsOutcome>. The <Expected> segment is a concrete state/value (IsInteractable, IsNotInteractable), never a condition word (Matches…, …WhenNonEmpty, DependingOn…).
  3. Within one partition, consolidate multiple representatives (and that partition's boundary values) into one parameterized method — they share the outcome (see Parameterized tests).

Worked exampledrawPileButton.interactable driven by the draw pile count:

PartitionRepresentative valuesExpected outcome
Empty (count == 0)0interactable is false
Non-empty (count ≥ 1)1 (boundary), 5interactable is true

Two partitions, two outcomes → two methods (NOT one parameterized method over {0, 1, 5}):

Test MethodVerification
Sync_DrawPileIsEmpty_DrawPileButtonIsNotInteractablethe draw pile button is not interactable
Sync_DrawPileIsExist_DrawPileButtonIsInteractable (count: {1, 5})the draw pile button is interactable

The non-empty method keeps both 1 and 5 because they share the outcome true; 1 is the partition's boundary value, 5 an interior representative.

Boundary value analysis locates where partitions meet. For a field valid in 1–99: partitions <1 / 1–99 / >99; boundaries 0, 1 and 99, 100. Fold each partition's boundary representatives into that partition's parameterized method (e.g., valid partition over {1, 99}) — never one method per boundary. When the spec does not differentiate behavior near an edge, one representative per partition suffices and boundaries can be skipped entirely (per the Boundary value analysis technique above).

Parameterized tests

When an equivalence partition includes multiple test cases — such as argument variations within the same partition or boundary values at the partition's edges — consolidate them into a single parameterized test. All cases must belong to the same equivalence partition and share the same expected outcome.

Specifying parameter name and values in the Test Method column — write values after the method name:

  • Bool parameter, all values: (flag: (bool))
  • Enum parameter, all values: (direction: (Direction))
  • Multiple parameters, all combinations (exhaustive): (param1: {0, 1, 2}, param2: {3, 4, 5}) — when a param is bool or enum covering all its values, use the shorthand in place of the braces: (flag: (bool), count: {0, 1, 5})
  • Multiple parameters, limited to specific combinations: ({param1: 0, param2: 3}, {param1: 1, param2: 4})
  • Three or more parameters each with many values: write (use pairwise) — the test-writing phase applies the pairwise (all-pairs) method to select a covering combination set

Do NOT over-consolidate: keep separate rows for tests that belong to different equivalence partitions or produce different expected outcomes.

Invalid partition

  • UI input validation — test invalid inputs that a user can enter through the UI (e.g., out-of-range values in a numeric text field). These represent real failure paths at the system boundary and must be tested.
  • Dependency error returns — whether to test error/failure paths from a dependency depends on its origin:
    • Library or framework (external, not owned by this project) → test it; use a stub to inject the error condition.
    • Game's own code (another component in this project) → skip; trust internal code correctness.
    • Uncertain → use AskUserQuestion to confirm with the user before designing test cases.

Testing randomness (PRNG-dependent SUT)

When the SUT consumes a pseudo-random number generator (UnityEngine.Random, System.Random, etc.), choose one of these strategies based on what the spec actually pins down:

  • Stub the PRNG — when the spec defines a deterministic mapping from random output to behavior (e.g., "≥0.5 → heads, <0.5 → tails"). Replace the PRNG with a stub that returns canned values; assert exact outcomes.
  • Range / bounds verification — when only the output range is specified (e.g., random spawn coordinates within a region). Assert with And/Range constraints, or Within/custom comparer for tolerances. Combine with Repeat attribute so flakiness isn't masked by a single lucky run.
  • Statistical-property verification — when the spec is about distribution shape (RPG damage variance, drop rates). Sample the SUT in a loop, compute statistics (mean, variance, histogram bucket counts), and assert on those. The test-helper package (com.nowsprinting.test-helper) provides lightweight sampling helpers; reach for MathNet.Numerics only when you need rigorous statistics.
  • Characteristic verification — when the SUT generates procedural content (e.g., roguelike maze). Don't assert exact output; assert structural properties the spec requires — e.g., "the exit is reachable from the entrance via path-finding," plus any algorithm-specific invariants.

Integration test perspectives

Verify from the user's perspective — assert on-screen display and UI interactions as much as possible. Avoid relying on internal state or property checks when user-visible behavior can be asserted instead.

When the test target is a scene or prefab (or an equivalent GameObject hierarchy assembled in test code) with interplaying components and assets, consider the following test perspectives:

  • Multi-frame event system interactions — behaviors triggered by Unity's event system that unfold across multiple frames
  • Scene transitions — behaviors that span or depend on scene loading and unloading
  • Asset linkage — runtime behaviors that emerge when placed components load or reference assets (e.g., ScriptableObject data, referenced prefabs)
  • UI operation sequences — click, drag, and other player operations that advance game mechanics over one or more frames
  • UI blocking — verify that UI elements behind a modal dialog or overlay are unreachable (blocked from interaction); conversely, verify those elements are reachable when no overlay is present
  • UI layout (layout assertion tests) — verify that buttons, toggles, and other interactive elements and text components do not overlap each other or overflow their parent containers, and that text does not overflow, using deterministic assertions:
    • A layout assertion test verifies a condition expressible as a deterministic assertion (e.g., "no overlap", "no overflow", "the element is reachable"); displayed content (card data, text length, item count) is a test input, and the pass criterion never varies with it. Contrast with visual verification tests, which verify conformance to a screen-specific design intent — positional relationships like "A is displayed to the right of B", color, and typography (font size, font style, font family) are design decisions likely to change, unsuited to strict assertions; legibility (text/background contrast) is impractical to assert.
    • Any layout bug expressible as a geometric predicate warrants a deterministic integration test assertion — e.g., "the element is within screen bounds", "elements do not overlap", "text does not overflow its container". Design these as integration tests only; do NOT additionally design a visual verification test for the same geometric property. Font size and style are typographic design properties, not geometric predicates, so this rule does not apply to them — but a BestFit readability floor (auto-shrunk text must not fall below a minimum readable size) has a content-independent pass criterion and stays a layout assertion test.
    • One test method per condition, not per perspective: do NOT split layout assertion perspectives (no overlap, no overflow, containment, reachability, BestFit floor) into one test method each. When perspectives share the same test condition (same scene/state/inputs/resolution), consolidate them into a single test method; the <Expected> segment may then be the generic LayoutIsCorrect (in the project language, e.g., レイアウトが正しいこと), and the Verification column lists every layout property verified (per the multi-property exception in Section 4).
    • Within-screen-bounds vs. within-container: design a "within screen bounds" check only for a screen's top-level container (a dialog, popup, context menu, or other root panel whose position or size is computed dynamically) — that is the element actually at risk of clipping past the screen edge. Do NOT design a "within screen bounds" check for an element nested inside such a container; instead check it for containment in its immediate parent container. If every element already sits within a parent that is itself verified within the screen, each nested element is transitively within the screen too — a per-element screen-bounds check on top of that is redundant.
    • Parameterizing a layout assertion test over all production assets (e.g., every card defined in the master data) also validates the content itself — a newly authored asset whose text overflows its view fails the test.
    • Do NOT verify positional relationships between elements or on-screen positions (e.g., "A is displayed to the right of B") — approximate positions have no meaningful pass/fail criterion, and precise coordinate checks are brittle. Use visual verification tests for these instead — including coarse screen-region checks (e.g., "the version label is in the bottom-right region"). Exception (only when the user explicitly instructs): a coarse screen-region predicate (e.g., "within the bottom-right region: below 10% of screen height and right of the horizontal center") becomes deterministic when the resolution is fixed as a test condition — design it as a layout assertion test (integration test). A single fixed resolution gives no confidence that the layout holds across screens, so parameterize over the expected resolutions (at least the largest and smallest supported resolutions and the widest and narrowest supported aspect ratios), one test case per resolution with the resolution in the <Condition> segment.

Reproduction tests (bug-fix tasks only)

When the task type is bug-fix, additionally apply the following during technique selection:

  • Reproduction test — design one test case that directly triggers the reported bug. Apply error guessing and, if the SUT has state, state transition testing to identify the minimal trigger condition. This test must fail before the fix and pass after.
  • Regression tests — identify adjacent behavior the fix might disturb, and apply the same techniques (equivalence partitioning, boundary value analysis, etc.) to derive coverage for those areas.

Cover and modify (refactoring tasks only)

For refactoring work, apply cover and modify: design regression coverage before changing the implementation. Treat every bug as an opportunity to grow the regression suite.

4. Create Test Cases

For each technique, derive coverage-aware test cases:

Language: <MethodName> must always match the production method name exactly — never translate it. <Condition>, <Expected>, and the Verification column prose follow the project language from the Language convention input. If no language is specified, default to English.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
21
Forks
3
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
test-designing-guide
Source
github.com/nowsprinting/unity-coding-skills