Skip to content
Testing
Skill

/test-writing-guide

Provides guidelines for writing test code for Unity projects. Make sure to use this skill whenever writing, creating, editing, or modifying test code files (files under Tests/). This includes implementing new tests, fixing test failures, adding test cases, or any task that

From plugin
unity-coding-skills
2110 skills3 agents
Install
$ npx -y skills add nowsprinting/unity-coding-skills --skill test-writing-guide --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/test-writing-guide

Context preview

The summary Claude sees to decide when to auto-load this skill.

Provides guidelines for writing test code for Unity projects. Make sure to use this skill whenever writing, creating, editing, or modifying test code files (files under Tests/). This includes implementing new tests, fixing test failures, adding test cases, or any task that

SKILL.md

test-writing-guide.SKILL.md
name: test-writing-guide
description: >-
  Provides guidelines for writing test code for Unity projects.
  Make sure to use this skill whenever writing, creating, editing, or modifying test code files (files under Tests/).
  This includes implementing new tests, fixing test failures, adding test cases, or any task that results in test code changes.
  Even for small edits or one-line fixes, load this skill to ensure test conventions are followed.
user-invocable: false
license: Unlicense
metadata:
  author: Koji Hasegawa

Guide for writing test code for Unity projects.

Rules

  • Before modifying any test file, check if the editor is in Play Mode. If it is, stop it using the `unity_play_control` tool first.
  • Never create `.meta` files. Unity editor creates them automatically.
  • When a test creates a `GameObject` or instantiates a prefab, add `[CreateScene]` to the test method (not required if `[LoadScene]` is already present).
  • When adding a test seam to production code (e.g., an `internal` accessor or a virtual override point to support injection), always wrap it with `#if UNITY_INCLUDE_TESTS` … `#endif` so it is excluded from non-test builds:
    #if UNITY_INCLUDE_TESTS
    internal void SetStateForTest(State state) => _state = state;
    #endif

Categories

  • When implementing tests designed as integration tests, add `[Category("Integration")]` to the test method.
  • When implementing tests designed as visual verification tests, add `[Category("VisualVerification")]` to the test method.
  • When implementing tests designed as acceptance tests (marked `(acceptance test)` in the test case design), add `[Category("Acceptance")]` to the test method.
  • For test methods that test the `internal` visibility method, add `[Category("Internal")]`.
  • For test methods that depend on animation timing or other timing-sensitive conditions that may cause instability on slow CPUs, add `[Category("IgnoreCI")]`.
  • For test methods that specify the `GameViewResolution` attribute, add `[Category("IgnoreCI")]`.

Multi-frame tests

When a game mechanic across frames (e.g., playing a card and waiting for its resolution), wait for the step to finish before asserting — not at a fixed frame count.

await DragCard(Cards[1], Enemies[0]);
while (battleDirector.IsPlaying) // wait until the played action fully resolves
    await Awaitable.NextFrameAsync();
Assert.That(battleState.Enemies[0].Hp, Is.LessThan(hpBefore));
  • To confirm step completion, use a production-side state machine or a "busy" signal (`IsPlaying`, coroutine flag, etc.). Do not use a fixed number of frames or `WaitForSeconds`.
  • When UniTask is available, `UniTask.WaitUntil` and `UniTask.WaitUntilValueChanged` are alternatives to the `while` loop:
  await UniTask.WaitUntil(() => isActive == false);
  await UniTask.WaitUntilValueChanged(this, x => x.isActive);
  • When an operation triggers a deferred `Destroy` or UI rebuild, advance one extra `await Awaitable.NextFrameAsync()` before asserting on the new hierarchy.

UI Tests

Layout assertion tests: verify UI layout with deterministic assertions

When a layout requirement is expressible as a deterministic assertion (*the element is within the screen, elements do not overlap, text does not overflow its container*), write it as a **layout assertion test** (an integration test), not a visual verification test. Displayed content (card data, text length, item count) is a test *input*: the pass criterion never varies with it. Leave to visual verification what does not suit a strict assertion: color, positional relationships like "A is to the right of B", on-screen position, and typography (font size, font style, font family) are design intent likely to change, and legibility (text/background contrast) is impractical to assert. Reasons:

  • **Deterministic pass/fail**: boolean assertions run unattended in CI without a human reading screenshots.
  • **Pins the specific bug**: an overlap assertion names the two elements; a screenshot cannot.
  • **Do NOT add a visual verification test for the same property** — verify each layout property in exactly one layer.

Choose the implementation means by what the condition is about:

  • **Rect comparison** — `Is.WithinScreen` (within-screen bounds), `Is.FullyWithin(container)` (containment in a parent), `Is.Not.Overlapping` (overlap between elements)
  • **Text overflow** — `Is.Not.TextOverflowing` (preferred/rendered size exceeds the rect, or characters are truncated)
  • **Raycast reachability** — `GameObjectFinder` with `reachable: true` (optionally with a paginator) proves the element is on screen and not covered by another element; use it when the condition is "the user can actually reach this element"

Before asserting, settle layout with `Canvas.ForceUpdateCanvases()` then `await Awaitable.NextFrameAsync()` — the former rebuilds pending layout/graphic geometry (rect sizes, text metrics); the latter waits for a real render pass, which is what a raycast reachability check needs, since a newly activated/deactivated `Graphic`'s `CanvasRenderer.depth` stays unset until then and `GraphicRaycaster` silently skips any candidate whose `depth == -1` — `ForceUpdateCanvases()` alone does not assign it.

See `test-helper.md` → **Layout constraints** for the constraint API (`Is.WithinScreen` / `Is.FullyWithin` / `Is.Not.Overlapping` / `Is.Not.TextOverflowing`).

Use GameObjectFinder instead of GameObject.Find

When finding a GameObject that the user interacts with, always use `TestHelper.UI.GameObjectFinder` instead of `UnityEngine.GameObject.Find`, `Object.FindFirstObjectByType`, `Object.FindAnyObjectByType`, and `Object.FindObjectOfType`. Reasons:

  • **Timing safety**: polls until the object appears, so tests pass even when GameObjects are instantiated asynchronously or on the next frame
  • **Reachability and interactability**: verifies the object is actually reachable by the user and (optionally) intera
Read more
Ships withunity-coding-skills

A Claude Code plugin for Unity development that enables coding agents to work autonomously through a test-first workflow — writing reliable, maintainable tests before production code, then iterating to completion without constant oversight.

Get the whole plugin
Stats
21
Stars
3
Forks
Active
Maintenance
C#
Language
Unlicense
License
1d ago
Last commit
3mo ago
Created

Repo: nowsprinting/unity-coding-skills

Other skills on unity-coding-skills.