test-helpers skill
Generate engine-specific test helper libraries — assertion utilities, factory functions, mocks in tests/helpers/. Reduces boilerplate.
Is the test-helpers skill safe?
Clean: nothing in its files matched our rules. We read 1 file in the folder on 2026-09-28.
No findings.
Install the test-helpers skill
A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.
git clone --depth 1 https://github.com/Donchitos/Claude-Code-Game-Studios.git /tmp/Claude-Code-Game-Studios mkdir -p ~/.claude/skills cp -r /tmp/Claude-Code-Game-Studios/.claude/skills/test-helpers ~/.claude/skills/test-helpers
In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub
The instructions your agent would load
SKILL.md as published, without the frontmatter. Read it on GitHub
!bash "${CLAUDESKILLDIR}/../../hooks/yaml-helper.sh" resolve_config --keys automation
Automation mode: Resolve modes.automation (project.local.yaml → project.yaml → default collaborative). Every AskUserQuestion call and every file write follows .claude/docs/automation-modes.md (collaborative asks always · guided major-only · autonomous logs and proceeds; automationalwaysask categories always prompt).
Test Helpers
Writing test cases is faster and more consistent when common setup, teardown, and assertion patterns are abstracted into helpers. This skill generates a tests/helpers/ library tailored to the project's actual engine, language, and systems — so every developer writes less boilerplate and more assertions.
Output: tests/helpers/ directory with engine-specific helper files
When to run:
- After /test-setup scaffolds the framework (first time)
- When multiple test files repeat the same setup boilerplate
- When starting to write tests for a new system
Assertions: use the project's test framework, never bare assert()
Generated helpers must assert through the configured test framework's assertion API, resolved from testing.framework — not through GDScript's built-in assert().
Two things go wrong with a bare assert() in a helper, and both are silent:
records a failure and continues, so one broken expectation yields one red test. A bare assert() halts execution, so the first failure hides every result after it.
- It aborts the run instead of failing the test. A framework assertion
stops asserting entirely in exactly the build you most want checked, and does so without any error — the tests still "pass".
- It is stripped in release builds. A helper library built on assert()
Look up the assertion form for the configured framework before generating code. Do not copy the forms in this file or in other skills' examples as authoritative — they are illustrative and have drifted (asserteq, asserttrue and assert_that all appear across the repo). If you cannot confirm the correct API for the project's framework, say so and generate no helper rather than guessing.
Helpers must not extend the framework's test-suite base class. A file in tests/helpers/ that extends the suite type is discovered by the runner as a test suite containing zero tests. Helpers are plain classes; only real test files extend the suite.
1. Parse Arguments
Modes:
(e.g., /test-helpers combat)
- /test-helpers [system-name] — generate helpers for a specific system
system-specific helpers); use this on first run
- /test-helpers all — generate helpers for all systems with test files
- /test-helpers scaffold — generate only the base helper library (no
- No argument — run scaffold if no helpers exist, else all
2. Detect Engine and Language
Read from project.yaml first, falling back to .claude/docs/technical-preferences.md for any key that is absent or empty:
- engine.name (else the Engine: value)
- engine.language (else the Language: value)
- testing.framework (else Framework: from the Testing section)
If the engine is not configured in either source: "Engine not configured. Run /setup-engine first."
3. Load Existing Test Patterns
Scan the test directory for patterns already in use:
Glob pattern="tests/**/*_test.*" (all test files)For a representative sample (up to 5 files), read the test files and extract:
- Setup patterns (how before_each / setUp / fixtures are written)
- Common assertion patterns (what is being asserted most often)
- Object creation patterns (how game objects or scenes are instantiated in tests)
- Mock/stub patterns (how dependencies are replaced)
This ensures generated helpers match the project's existing style, not a generic template.
Also read:
- design/gdd/systems-index.md — to know which systems exist
- In-scope GDD(s) — to understand what data types and values need testing
- docs/architecture/tr-registry.yaml — to map requirements to tested systems
4. Generate Engine-Specific Helpers
Godot 4 (GDUnit4 / GDScript)
FAILIF(...) in the example below is a PLACEHOLDER, not an API.** It marks
the one line you must resolve from the project's actual test framework before
emitting any of this, per the rule in §2. Substitute the framework's real
failure call — the form that **registers a failure with the runner and lets the
suite continue** — and delete the marker comment.
It is deliberately not spelled assertthat, asserteq or asserttrue.**
All three appear somewhere in this repo, they disagree, and **GdUnit4's API is
not covered by docs/engine-reference/** — so writing any of them here would be
asserting an API this repo cannot source, in the file whose whole job is to stop
people doing that. If you cannot confirm the correct call for the configured
framework, §2 already tells you the answer: generate no helper. An
unresolved FAIL_IF reaching disk is a bug; a missing helper is a task.
Worked examples must obey §2 too — a bare assert() here would be the exact
form §2 forbids, sixty lines below §2 forbidding it. A rule stated in prose
does not reach its own worked example unless someone makes it.
Base helper (tests/helpers/game_assertions.gd):
## Game-specific assertion utilities for [Project Name] tests.
## Domain-specific helpers built on the project's test framework.
##
## Usage (static — no instance needed):
## GameAssertions.assert_in_range(entity.health, 0, entity.max_health, "health")
class_name GameAssertions
extends RefCounted
## Assert a value is within the inclusive range [min_val, max_val].
## Use for any formula output that has defined bounds in a GDD.
static func assert_in_range(
value: float,
min_val: float,
max_val: float,
label: String = "value"
) -> void:
# FRAMEWORK ASSERT — resolve per the rule above; do not emit `assert()`.
FAIL_IF(
not (value >= min_val and value <= max_val),
"%s %.2f is outside expected range [%.2f, %.2f]" % [label, value, min_val, max_val]
)
## Assert a signal was emitted during a callable block.
## Usage: assert_signal_emitted(entity, "health_changed", func(): entity.take_damage(10))
static func assert_signal_emitted(
obj: Object,
signal_name: String,
action: Callable
) -> void:
var emitted := false
obj.connect(signal_name, func(_args): emitted = true)
action.call()
# FRAMEWORK ASSERT — resolve per the rFactory helper (tests/helpers/game_factory.gd):
## Factory functions for creating test game objects.
## Returns minimal objects configured for unit testing (no scene tree required).
##
## Usage: var player = GameFactory.make_player(health: 100)
class_name GameFactory
extends RefCounted
## Create a minimal player-like object for testing.
## Override fields as needed.
static func make_player(health: int = 100) -> Node:
var player = Node.new()
player.set_meta("health", health)
player.set_meta("max_health", health)
return playerScene helper (tests/helpers/scenerunnerhelper.gd):
## Utilities for scene-based integration tests.
## Wraps GdUnitSceneRunner for common patterns.
class_name SceneRunnerHelper
extends GdUnitTestSuite
## Load a scene and wait one frame for _ready() to complete.
func load_scene_and_wait(scene_path: String) -> Node:
var scene = load(scene_path).instantiate()
add_child(scene)
await get_tree().process_frame
return sceneUnity (NUnit / C#)
Base helper (tests/helpers/GameAssertions.cs):
More skills from Donchitos/Claude-Code-Game-Studios
- AadoptBrownfield audit — do existing artifacts actually work? Numbered migration plan. Unlike /project-stage-detect, checks compliance not existence.
- Aarchitecture-decisionCreate an ADR documenting a technical decision: context, alternatives considered, consequences.
- Aarchitecture-reviewTraceability matrix mapping GDD requirements to ADRs. Finds gaps, cross-ADR conflicts, engine compatibility. PASS/CONCERNS/NOT ASSESSED/FAIL.
- Aart-bibleAuthor the Art Bible — visual identity gating asset production. Run before /map-systems.
- Aasset-auditAudit assets against naming conventions, file size budgets, format standards. Finds orphaned assets, missing references.
- Aasset-specPer-asset visual specs plus AI generation prompts from GDDs and character profiles. After the art bible.
- Abalance-checkFind balance outliers, broken progressions, degenerate strategies, economy imbalances in formulas and data. 'Check game balance'.
- AbrainstormGuided concept ideation using professional studio techniques, player psychology, creative exploration.
- Abug-reportStructured bug report from a description, or analyze code for potential bugs. Reproduction steps, severity.
- Abug-triageRe-evaluate open bugs — priority vs severity, assign to sprints, surface systemic trends. Run when the count grows.
- AchangelogAuto-generate a changelog from git commits and sprint data. Internal and player-facing versions.
- Acode-reviewArchitectural code review — coding standards, SOLID, testability, performance concerns.