test-helpers▌
Donchitos/Claude-Code-Game-Studios · updated Apr 16, 2026
### Test Helpers
- ›description: "Generate engine-specific test helper libraries for the project's test suite. Reads existing test patterns and produces tests/helpers/ with assertion utilities, factory functions, and moc
- ›argument-hint: "[system-name | all | scaffold]"
- ›allowed-tools: Read, Glob, Grep, Write
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-setupscaffolds the framework (first time) - When multiple test files repeat the same setup boilerplate
- When starting to write tests for a new system
1. Parse Arguments
Modes:
/test-helpers [system-name]— generate helpers for a specific system (e.g.,/test-helpers combat)/test-helpers all— generate helpers for all systems with test files/test-helpers scaffold— generate only the base helper library (no system-specific helpers); use this on first run- No argument — run
scaffoldif no helpers exist, elseall
2. Detect Engine and Language
Read .claude/docs/technical-preferences.md and extract:
Engine:valueLanguage:valueFramework:from the Testing section
If engine is not configured: "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)
Base helper (tests/helpers/game_assertions.gd):
## Game-specific assertion utilities for [Project Name] tests.
## Extends GdUnitAssertions with domain-specific helpers.
##
## Usage:
## var assert = GameAssertions.new()
## assert.health_in_range(entity, 0, entity.max_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:
assert(
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()
assert(emitted, "Expected signal '%s' to be emitted, but it was not." % signal_name)
## Assert that a callable does NOT emit a signal.
static func assert_signal_not_emitted(
obj: Object,
signal_name: String,
action: Callable
) -> void:
var emitted := false
obj.connect(signal_name, func(_args): emitted = true)
action.call()
assert(not emitted, "Expected signal '%s' NOT to be emitted, but it was." % signal_name)
## Assert a node exists at path within a parent.
static func assert_node_exists(parent: Node, path: NodePath) -> void:
assert(
parent.has_node(path),
"Expected node at path '%s' to exist." % str(path)
)
Factory 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 player
Scene helper (tests/helpers/scene_runner_helper.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 scene
Unity (NUnit / C#)
Base helper (tests/helpers/GameAssertions.cs):
using NUnit.Framework;
using UnityEngine;
/// <summary>
/// Game-specific assertion utilities for [Project Name] tests.
/// Extends NUnit's Assert with domain-specific helpers.
/// </summary>
public static class GameAssertions
{
/// <summary>
/// Assert a value is within an inclusive range [min, max].
/// Use for any formula output defined in GDD Formulas sections.
/// </summary>
public static void AssertInRange(float value, float min, float max, string label = "value")
{
Assert.That(value, Is.InRange(min, max),
$"{label} ({value:F2}) is outside expected range [{min:F2}, {max:F2}]");
}
/// <summary>Assert a UnityEvent or C# event was raised during an action.</summary>
public static void AssertEventRaised(ref bool wasCalled, System.Action action, string eventName)
{
wasCalled = false;
action();
Assert.IsTrue(wasCalled, $"Expected event '{eventName}' to be raised, but it was not.");
}
/// <summary>Assert a component exists on a GameObject.</summary>
public static void AssertHasComponent<T>(GameObject obj) where T : Component
{
var component = obj.GetComponent<T>();
Assert.IsNotNull(component,
$"Expected GameObject '{obj.name}' to have component {typeof(T).Name}.");
}
}
Factory helper (tests/helpers/GameFactory.cs):
using UnityEngine;
/// <summary>
/// Factory methods for creating minimal test objects without loading scenes.
/// </summary>
public static class GameFactory
{
/// <summary>Create a minimal GameObject with a named component for testing.</summary>
public static GameObject MakeGameObject(string name = "TestObject")
{
var go = new GameObject(name);
return go;
}
/// <summary>
/// Create a ScriptableObject of type T for data-driven tests.
/// Dispose with Object.DestroyImmediate after test.
/// </summary>
public static T MakeScriptableObject<T>() where T : ScriptableObject
{
return ScriptableObject.CreateInstance<T>();
}
}
Unreal Engine (C++)
Base helper (tests/helpers/GameTestHelpers.h):
#pragma once
#include "CoreMinimal.h"
#include "Misc/AutomationTest.h"
/**
* Game-specific assertion macros and helpers for [Project Name] automation tests.
* Include in any test file that needs domain-specific assertions.
*
* Usage:
* GAME_TEST_ASSERT_IN_RANGE(TestName, DamageValue, 10.0f, 50.0f, TEXT("Damage"));
*/
// Assert a float value is within inclusive range [Min, Max]
#define GAME_TEST_ASSERT_IN_RANGE(TestName, Value, Min, Max, Label) \
TestTrue( \
FString::Printf(TEXT("%s (%.2f) in range [%.2f, %.2f]"), Label, Value, Min, Max), \
(Value) >= (Min) && (Value) <= (Max) \
)
// Assert a UObject pointer is valid (not null, not garbage collected)
#define GAME_TEST_ASSERT_VALID(TestName, Ptr, Label) \
TestTrue( \
FString::Printf(TEXT("%s is valid"), Label), \
IsValid(Ptr) \
)
// Assert an Actor is in the world (spawned successfully)
#define GAME_TEST_ASSERT_SPAWNED(TestName, ActorPtr, ClassName) \
TestNotNull( \
FString::Printf(TEXT("Spawned actor of class %s"), TEXT(#ClassName)), \
ActorPtr \
)
/**
* Helper to create a minimal test world.
* Remember to call World->DestroyWorld(false) in teardown.
*/
namespace GameTestHelpers
{
inline UWorld* CreateTestWorld(const FString& WorldName = TEXT("TestWorld"))
{
UWorld* World = UWorld::CreateWorld(EWorldType::Game, false);
FWorldContext& WorldContext = GEngine->CreateNewWorldContext(EWorldType::Game);
WorldContext.SetCurrentWorld(World);
return World;
}
}
5. Generate System-Specific Helpers
For [system-name] or all modes, generate a helper per system:
Read the system's GDD to extract:
- Data types (entity types, component names)
- Formula variables and their bounds
- Common test scenarios mentioned in Edge Cases
Generate tests/helpers/[system]_factory.[ext] with factory functions
specific to that system's objects.
Example pattern for a combat system (Godot/GDScript):
## Factory and assertion helpers for Combat system tests.
## Generated by /test-helpers combat on [date].
## Based on: design/gdd/combat.md
class_name CombatTestFactory
extends RefCounted
const DAMAGE_MIN := 0
const DAMAGE_MAX := 999 # From GDD: damage formula upper bound
## Create a minimal attacker object for damage formula tests.
static func make_attacker(attack: float = 10.0, crit_chance: float = 0.0) -> Node:
var attacker = Node.new()
attacker.set_meta("attack", attack)
attacker.set_meta("crit_chance", crit_chance)
return attacker
## Create a minimal target object for damage receive tests.
static func make_target(defense: float = 0.0, health: float = 100.0) -> Node:
var target = Node.new()
target.set_meta("defense", defense)
target.set_meta("health", health)
target.set_meta("max_health", health)
return target
## Assert damage output is within GDD-specified bounds.
static func assert_damage_in_bounds(damage: float) -> void:
GameAssertions.assert_in_range(damage, DAMAGE_MIN, DAMAGE_MAX, "damage")
6. Write Output
Present a summary of what will be created:
## Test Helpers to Create
Base helpers (engine: [engine]):
- tests/helpers/game_assertions.[ext]
- tests/helpers/game_factory.[ext]
[engine-specific extras]
System helpers ([mode]):
- tests/helpers/[system]_factory.[ext] ← from [system] GDD
Ask: "May I write these helper files to tests/helpers/?"
Never overwrite existing files. If a file already exists, report:
"Skipping [path] — already exists. Remove the file manually if you want it
regenerated."
After writing: Verdict: COMPLETE — helper files created.
"Helper files created. To use them in a test:
- Godot:
class_nameis auto-imported — no explicit import needed - Unity: Add
usingdirective or reference the test assembly - Unreal:
#include \"tests/helpers/GameTestHelpers.h\""
Collaborative Protocol
- Never overwrite existing helpers — they may contain hand-written customisations. Only generate new files that don't exist yet
- Generated code is a starting point — the generated factory functions use metadata patterns for simplicity; adapt to the actual class structure once the code exists
- Helpers should reflect the GDD — bounds and constants in helpers should trace to GDD Formulas sections, not invented values
- Ask before writing — always confirm before creating files in
tests/
Next Steps
- Run
/test-setupif the test framework has not been scaffolded yet. - Use
/dev-storyto implement stories — helpers reduce boilerplate in new test files. - Run
/skill-testto validate other skills that may need helper coverage.
Ratings
4.6★★★★★44 reviews- ★★★★★Shikha Mishra· Dec 24, 2024
Solid pick for teams standardizing on skills: test-helpers is focused, and the summary matches what you get after install.
- ★★★★★Kabir Haddad· Dec 16, 2024
Registry listing for test-helpers matched our evaluation — installs cleanly and behaves as described in the markdown.
- ★★★★★Sophia Wang· Dec 12, 2024
I recommend test-helpers for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
- ★★★★★Isabella Agarwal· Dec 8, 2024
test-helpers fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
- ★★★★★Aanya Liu· Nov 27, 2024
test-helpers has been reliable in day-to-day use. Documentation quality is above average for community skills.
- ★★★★★Kofi Anderson· Nov 19, 2024
Registry listing for test-helpers matched our evaluation — installs cleanly and behaves as described in the markdown.
- ★★★★★Yash Thakker· Nov 3, 2024
Registry listing for test-helpers matched our evaluation — installs cleanly and behaves as described in the markdown.
- ★★★★★William Shah· Nov 3, 2024
Keeps context tight: test-helpers is the kind of skill you can hand to a new teammate without a long onboarding doc.
- ★★★★★Dhruvi Jain· Oct 22, 2024
test-helpers reduced setup friction for our internal harness; good balance of opinion and flexibility.
- ★★★★★Amelia Brown· Oct 22, 2024
test-helpers is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
showing 1-10 of 44