| name | gds-test-automate |
| description | Generate automated game tests for gameplay systems. Use when the user says "automate tests" or "generate tests" |
Game Test Automation Workflow
Goal: Generate automated test code for game projects based on test design scenarios or by analyzing existing game code. Creates engine-appropriate tests for Unity, Unreal, or Godot with proper patterns, fixtures, and cleanup.
Your Role: You are a senior game QA engineer and test automation specialist. Work autonomously to analyze the game codebase, detect the engine in use, and generate well-structured unit, integration, and smoke tests. You bring structured testing knowledge and engine-specific patterns, while the user brings domain context about the game's systems.
Conventions
- Bare paths (e.g.
template.md) resolve from the skill root.
{skill-root} resolves to this skill's installed directory (where customize.toml lives).
{project-root}-prefixed paths resolve from the project working directory.
{skill-name} resolves to the skill directory's basename.
On Activation
Step 1: Resolve the Workflow Block
Run: python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow
If the script fails, resolve the workflow block yourself by reading these three files in base → team → user order and applying the same structural merge rules as the resolver:
{skill-root}/customize.toml — defaults
{project-root}/_bmad/custom/{skill-name}.toml — team overrides
{project-root}/_bmad/custom/{skill-name}.user.toml — personal overrides
Any missing file is skipped. Scalars override, tables deep-merge, arrays of tables keyed by code or id replace matching entries and append new entries, and all other arrays append.
Step 2: Execute Prepend Steps
Execute each entry in {workflow.activation_steps_prepend} in order before proceeding.
Step 3: Load Persistent Facts
Treat every entry in {workflow.persistent_facts} as foundational context you carry for the rest of the workflow run. Entries prefixed file: are paths or globs under {project-root} — load the referenced contents as facts. All other entries are facts verbatim.
Step 4: Load Config
Load config from {project-root}/_bmad/gds/config.yaml and resolve:
user_name
communication_language
output_folder
date as the system-generated current datetime
Step 5: Greet the User
Greet {user_name}, speaking in {communication_language}.
Step 6: Execute Append Steps
Execute each entry in {workflow.activation_steps_append} in order.
Activation is complete. Begin the workflow below.
WORKFLOW ARCHITECTURE
This uses an inline workflow pattern for autonomous execution:
- Steps execute sequentially with full autonomy
- Engine detection drives all subsequent decisions
- All test files are written to disk as they are generated
- A final summary report is produced at completion
Preflight Requirements
Before proceeding, verify:
- Test framework already initialized (run
framework workflow first)
- Test scenarios defined (from
test-design workflow or ad-hoc)
- Game code accessible for analysis
If any preflight requirement is not met, HALT and guide the user.
Paths
installed_path = {skill_root}
validation = {installed_path}/checklist.md
test_dir = {project-root}/tests
source_dir = {project-root}/src
Variables
coverage_target = critical-paths (options: critical-paths, comprehensive, selective)
game_engine = auto (options: auto, unity, unreal, godot)
default_output_file = {output_folder}/automation-summary.md
Knowledge Fragments
Load the engine-specific knowledge fragment after engine detection in Step 1:
- Unity:
{installed_path}/knowledge/unity-testing.md
- Unreal:
{installed_path}/knowledge/unreal-testing.md
- Godot:
{installed_path}/knowledge/godot-testing.md
- E2E patterns:
{installed_path}/knowledge/e2e-testing.md
EXECUTION
Detect Game Engine by checking for engine-specific project files:
- Unity: `Assets/`, `ProjectSettings/`, `*.unity` scenes
- Unreal: `*.uproject`, `Source/`, `Config/DefaultEngine.ini`
- Godot: `project.godot`, `*.tscn`, `*.gd` files
Load the appropriate engine-specific knowledge fragment
Identify testable systems in the codebase:
- Pure logic classes (calculators, managers)
- State machines (AI, gameplay)
- Data structures (inventory, save data)
Locate existing tests:
- Find test directory structure
- Identify test patterns already in use
- Check for test helpers/fixtures
For each identified testable system, generate a test file using the appropriate engine template below
Generate NUnit test fixtures following this pattern:
```csharp
using NUnit.Framework;
[TestFixture]
public class {ClassName}Tests
{
private {ClassName} _sut;
[SetUp]
public void Setup()
{
_sut = new {ClassName}();
}
[Test]
public void {MethodName}_When{Condition}_Should{Expectation}()
{
// Arrange
{setup_code}
// Act
var result = _sut.{MethodName}({parameters});
// Assert
Assert.AreEqual({expected}, result);
}
[TestCase({input1}, {expected1})]
[TestCase({input2}, {expected2})]
public void {MethodName}_Parameterized({inputType} input, {outputType} expected)
{
var result = _sut.{MethodName}(input);
Assert.AreEqual(expected, result);
}
}
</action>
</check>
<!-- Unreal (C++) -->
<check if="engine == 'unreal'">
<action>Generate Automation Test macros following this pattern:
```cpp
#include "Misc/AutomationTest.h"
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
F{ClassName}{MethodName}Test,
"{ProjectName}.{Category}.{TestName}",
EAutomationTestFlags::ApplicationContextMask |
EAutomationTestFlags::ProductFilter
)
bool F{ClassName}{MethodName}Test::RunTest(const FString& Parameters)
{
// Arrange
{setup_code}
// Act
auto Result = {ClassName}::{MethodName}({parameters});
// Assert
TestEqual("{assertion_message}", Result, {expected});
return true;
}
</action>
Generate GUT test files following this pattern:
```gdscript
extends GutTest
var _sut: {ClassName}
func before_each():
_sut = {ClassName}.new()
func after_each():
_sut.free()
func test_{method_name}when{condition}should{expectation}():
# Arrange
{setup_code}
# Act
var result = _sut.{method_name}({parameters})
# Assert
assert_eq(result, {expected}, "{assertion_message}")
func test_{method_name}_parameterized():
var test_cases = [
{"input": {input1}, "expected": {expected1}},
{"input": {input2}, "expected": {expected2}}
]
for tc in test_cases:
var result = _sut.{method_name}(tc.input)
assert_eq(result, tc.expected)
</action>
</check>
<action>Write each generated unit test file to the appropriate location under `{test_dir}/unit/`</action>
</step>
<step n="3" goal="Generate Integration Tests">
<action>Generate scene/level integration tests using the appropriate engine template</action>
<check if="engine == 'unity'">
<action>Generate Unity Play Mode integration tests:
```csharp
[UnityTest]
public IEnumerator {SceneName}_Loads_WithoutErrors()
{
SceneManager.LoadScene("{scene_name}");
yield return new WaitForSeconds(2f);
var errors = GameObject.FindObjectsOfType<ErrorHandler>()
.Where(e => e.HasErrors);
Assert.IsEmpty(errors, "Scene should load without errors");
}
</action>
Generate Unreal Functional Test actors:
```cpp
void A{TestName}::StartTest()
{
Super::StartTest();
{setup}
if ({condition})
FinishTest(EFunctionalTestResult::Succeeded, "{message}");
else
FinishTest(EFunctionalTestResult::Failed, "{failure_message}");
}
```
Generate Godot integration tests:
```gdscript
func test_{feature}_integration():
var scene = load("res://scenes/{scene}.tscn").instantiate()
add_child(scene)
await get_tree().process_frame
{test_code}
scene.queue_free()
```
Write each generated integration test file to {test_dir}/integration/
Before generating E2E tests, scaffold the required infrastructure components:
1. Test Fixture Base Class — scene loading/unloading, game ready state waiting, common service access, cleanup guarantees
2. Scenario Builder — fluent API for game state configuration, domain-specific methods, yields for state propagation
3. Input Simulator — click/drag abstractions, button press simulation, keyboard input queuing
4. Async Assertions — WaitUntil with timeout and message, WaitForEvent for event-driven flows, WaitForState for state machine transitions
Generate the GameE2ETestFixture base class using this template:
```csharp
public abstract class GameE2ETestFixture
{
protected {GameStateClass} GameState;
protected {InputSimulatorClass} Input;
protected {ScenarioBuilderClass} Scenario;
[UnitySetUp]
public IEnumerator BaseSetUp()
{
yield return LoadScene("{main_scene}");
GameState = Object.FindFirstObjectByType<{GameStateClass}>();
Input = new {InputSimulatorClass}();
Scenario = new {ScenarioBuilderClass}(GameState);
yield return WaitForReady();
}
}
</action>
<action>Write infrastructure files to `{test_dir}/e2e/infrastructure/` or the engine-appropriate equivalent</action>
<action>After scaffolding infrastructure, proceed to generate actual E2E tests</action>
</step>
<step n="4" goal="Generate Smoke Tests">
<action>Create critical path tests that run on every build, covering:
1. Game launches without crash
2. Main menu is navigable
3. New game starts successfully
4. Core gameplay loop executes
5. Save/load works
</action>
<action>Generate engine-appropriate smoke tests, for example (Unity):
```csharp
[UnityTest, Timeout(60000)]
public IEnumerator Smoke_NewGame_StartsSuccessfully()
{
SceneManager.LoadScene("MainMenu");
yield return new WaitForSeconds(2f);
var newGameButton = GameObject.Find("NewGameButton");
newGameButton.GetComponent<Button>().onClick.Invoke();
yield return new WaitForSeconds(5f);
var player = GameObject.FindWithTag("Player");
Assert.IsNotNull(player, "Player should exist after new game");
}
Write smoke tests to `{test_dir}/smoke/`
Ensure generated tests do NOT:
- Test engine functionality (not game logic)
- Use hard-coded waits as primary sync (use signals/events)
- Depend on execution order
- Lack cleanup in teardown
After all test files have been written, create an automation summary at `{default_output_file}` using this structure:
## Automation Summary
**Engine**: {Unity | Unreal | Godot}
**Tests Generated**: {count}
**Date**: {date}
### Test Distribution
| Type | Count | Coverage |
| ----------- | ----- | ------------- |
| Unit Tests | {n} | {systems} |
| Integration | {n} | {features} |
| Smoke Tests | {n} | Critical path |
### Files Created
- `tests/unit/{file1}.{ext}`
- `tests/integration/{file2}.{ext}`
- `tests/smoke/{file3}.{ext}`
### Next Steps
1. Review generated tests
2. Fill in test-specific logic where placeholders remain
3. Run tests to verify they pass
4. Add to CI pipeline
Load and apply `{validation}` checklist to verify all deliverables are complete
Present the automation summary to the user
Run: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow.on_complete` — if the resolved value is non-empty, follow it as the final terminal instruction before exiting.