gdunit-driver
Run gdUnit4 unit tests and parse results into structured output. Use this skill after writing or modifying code to verify correctness via unit tests, when diagnosing test failures, or when writing new test files. Triggers: "run tests", "test fails", "write a test", any gdUnit4/unit test mention.
Install / Use
npx skills add RandallLiuXin/GodotMaker --skill gdunit-driverInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Content & MediaSupported Platforms
Tags
Our assessment of gdunit-driver
gdunit-driver scores 92/100 on our quality scale, 268th of 1,215 Content & Media skills we index (top 23%).
Its SKILL.md is 13 KB long, well organised into 46 sections with 14 code examples: a thorough specification that gives an agent plenty to work with.
It has 549 GitHub stars, a meaningful sign that others use it.
Maintenance, license and trust
- The repository was last updated 16 days ago, so gdunit-driver is actively maintained.
- No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
- Its trust signals score 88/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
gdunit-driver compared with similar skills
All 4 of these similar skills score higher than gdunit-driver; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| gdunit-driver (this skill)by RandallLiuXin | 92 | 549 | 16d ago | SKILL.md |
| siyuanby siyuan-note | 100 | 46.6k | today | MCP Server |
| algorithmic-artby anthropics | 100 | 177.9k | 11d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 11d ago | SKILL.md |
| designby nextlevelbuilder | 100 | 130.2k | 12d ago | SKILL.md |
Frequently asked questions
- How do I install gdunit-driver?
- Run
npx skills add RandallLiuXin/GodotMaker --skill gdunit-driver. The install tabs above show the steps for each supported agent. - Which AI agents does gdunit-driver work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is gdunit-driver safe to use?
- It declares no license and scores 88/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is gdunit-driver still maintained?
- The repository was last updated 16 days ago, so gdunit-driver is actively maintained.
Skill content
View source on GitHubname: gdunit-driver description: | Run gdUnit4 unit tests and parse results into structured output. Use this skill after writing or modifying code to verify correctness via unit tests, when diagnosing test failures, or when writing new test files. Triggers: "run tests", "test fails", "write a test", any gdUnit4/unit test mention. Supports both GDScript (.gd) and C# (.cs) test files.
gdUnit4 Test Driver
$ARGUMENTS
1. Locate the Godot executable
Read the path from the project's config file. This avoids hardcoding paths that differ per machine.
# From the project root:
python tools/agent_runtime.py godot_path
2. Run tests
The CLI runner across all supported versions (v4.x, v5.x, v6.x) is addons/gdUnit4/bin/GdUnitCmdTool.gd. The path uses capital-U gdUnit4/ to match the upstream repo layout — Windows is case-insensitive but Godot's global script registry de-duplicates by exact path string, so a casing mismatch between the runner invocation and the on-disk directory triggers Class "..." hides a global script class parse errors and a non-zero exit.
# Single file
"<godot_path>" --headless -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
--add res://test/test_example.gd --ignoreHeadlessMode
# Multiple files
"<godot_path>" --headless -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
--add res://test/test_physics.gd --add res://test/test_spawner.gd \
--ignoreHeadlessMode
# All tests in a directory
"<godot_path>" --headless -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
--add res://test/ --ignoreHeadlessMode
Notes:
- Include
--ignoreHeadlessModefor headless runs. - Use
--addto enqueue test files or directories (repeat the flag for multiples). - Runner path needs the
res://prefix and the capital-Uaddons/gdUnit4/casing. - No
::methodsyntax for single test methods — run the whole file instead.
C# tests
GdUnitCmdTool.gd supports C# test files too, but ensure dotnet build passes first — gdUnit4 runs compiled assemblies, not source files.
dotnet build && "<godot_path>" --headless -s res://addons/gdUnit4/bin/GdUnitCmdTool.gd \
--add res://test/csharp/TestExample.cs --ignoreHeadlessMode
Useful flags
| Flag | Purpose |
|------|---------|
| --add <path> | Add test path to execution (file or directory; repeat to enqueue multiple) |
| --ignoreHeadlessMode | Allow headless execution |
| --report-directory <path> | Override report output directory |
Timeout
gdUnit4 has a default test timeout (configurable in GdUnitSettings). If tests hang:
- Check for infinite loops or unresolved
awaitcalls - Tests involving scene tree operations need explicit timeouts on await calls
- Kill the process after 120 seconds if no output appears
3. Parse results
Stdout parsing
GdUnitCmdTool.gd output contains ANSI color codes — strip them before parsing. Format:
Run Test Suite res://test/test_example.gd
Run Test: res://test/test_example.gd > test_basic_math :PASSED 38ms
Run Test: res://test/test_example.gd > test_will_fail :FAILED 39ms
Report:
line <n/a>: Expecting:
'2'
but was
'1'
Statistics: | 2 tests cases | 0 error | 1 failed | 0 flaky | 0 skipped | 0 orphans |
Executed test suites: (1/1)
Executed test cases: (2/2)
Total time: 128ms
Exit code: 100
Notes:
- Per-test lines start with
Run Test:; suite lines withRun Test Suite. - Duration is reported in milliseconds (
38ms). - Failure line numbers may show
line <n/a>instead of exact lines. - Summary uses
Statistics:with pipe-delimited counts. - Exit code 100 = test failures (not 1).
Extract from each test line:
- Name: the
test_*function name (after>). - Status:
PASSED,FAILED,SKIPPED,ERROR. - Duration:
Nmsafter the status. - Failure message: indented
Report:lines following a FAILED test (assertion details + source location if available).
JUnit XML report
Use --report-directory <path> for JUnit XML reports. The default report
directory is res://reports/, which maps to project-root reports/.
<testsuites>
<testsuite name="TestExample" tests="4" failures="1" errors="0" skipped="1">
<testcase name="test_basic_math" classname="TestExample" time="0.002"/>
<testcase name="test_will_fail" classname="TestExample" time="0.003">
<failure message="Expecting '2' but was '1'" type="AssertionError">
at: res://test/test_example.gd:15
</failure>
</testcase>
</testsuite>
</testsuites>
Structured output format
Report results in this format:
## Test Results: test_example.gd
| Test | Status | Duration |
|------|--------|----------|
| test_basic_math | PASS | 0.002s |
| test_string_concat | PASS | 0.001s |
| test_will_fail | FAIL | 0.003s |
| test_skipped | SKIP | 0.000s |
**Summary: 4 total, 2 passed, 1 failed, 1 skipped, 0 errors**
### Failures
**test_will_fail** (res://test/test_example.gd:15)
> Expecting '2' but was '1'
Always include the file:line for failures — the agent (or user) needs this to navigate to the problem.
4. Writing tests
When the agent needs to write a new test file, follow these patterns.
GDScript test structure
# res://test/test_my_system.gd
extends GdUnitTestSuite
# Runs before each test
func before_test() -> void:
pass
# Runs after each test
func after_test() -> void:
pass
func test_example() -> void:
assert_int(2 + 2).is_equal(4)
func test_string_operations() -> void:
assert_str("hello").contains("ell")
Key assertion API
# Integers
assert_int(value).is_equal(expected)
assert_int(value).is_greater(threshold)
assert_int(value).is_between(low, high)
# Floats
assert_float(value).is_equal_approx(expected, 0.001)
# Strings
assert_str(value).is_equal(expected)
assert_str(value).contains(substring)
assert_str(value).starts_with(prefix)
# Booleans
assert_bool(value).is_true()
assert_bool(value).is_false()
# Objects
assert_that(value).is_not_null()
assert_that(value).is_instanceof(MyClass)
# Arrays
assert_array(arr).has_size(3)
assert_array(arr).contains([1, 2])
# Signals
await assert_signal(node).is_emitted("my_signal")
await assert_signal(node).wait_until(2.0).is_emitted("my_signal")
# Errors / Warnings (assert that code pushes expected error)
assert_error(callable).is_push_error("expected message")
Scene fixtures
When testing code that needs nodes or scene tree:
func test_with_scene() -> void:
# auto_free ensures cleanup even if test fails — prevents scene leaks
var scene = auto_free(load("res://scenes/player.tscn").instantiate())
add_child(scene)
# Now test with the live scene
assert_that(scene.get_node("Sprite2D")).is_not_null()
auto_free() is critical — without it, test failures leak nodes and eventually crash the runner.
Stub design
A stub class must expose every property and method the system-under-test reads or calls on it.
# WRONG — bare Node has no global_position, no velocity
var entity = auto_free(Node.new())
system.process_one(entity) # Invalid access to property "global_position"
# RIGHT — stub class carries the properties the system reads
var entity = auto_free(CharacterBody2D.new())
system.process_one(entity)
Grep the system code for every property and method it touches on the stubbed argument; each one must exist on the stub class.
Async tests
For code involving signals, timers, or physics frames:
func test_signal_emission() -> void:
var emitter = auto_free(SignalEmitter.new())
add_child(emitter)
emitter.trigger_action()
await assert_signal(emitter).wait_until(2.0).is_emitted("action_done")
func test_after_physics_frame() -> void:
var node = auto_free(MyNode.new())
add_child(node)
# Wait for physics to process
await get_tree().physics_frame
assert_float(node.position.x).is_greater(0.0)
Always pass a timeout to wait_until() — unbounded waits hang the test runner.
SceneRunner (frame and input simulation)
When tests need _process / _physics_process to actually execute, or need to simulate player input, use gdUnit4's SceneRunner. Without it, adding a node to the tree does NOT advance frames — process callbacks never fire.
GDScript:
func test_player_moves_on_input() -> void:
var runner := scene_runner("res://scenes/player.tscn")
# Simulate input action (matches Input Map)
runner.simulate_action_pressed("move_right")
await runner.simulate_frames(5)
var player = runner.scene()
assert_float(player.position.x).is_greater(0.0)
func test_physics_movement() -> void:
var runner := scene_runner("res://scenes/player.tscn")
runner.set_property("velocity", Vector2(100, 0))
# Advance 10 frames — _physics_process runs each frame
await runner.simulate_frames(10)
assert_float(runner.scene().position.x).is_greater(0.0)
C#:
[TestCase]
public async Task TestPlayerMovesOnInput()
{
var runner = ISceneRunner.Load("res://scenes/Player.tscn");
runner.SimulateActionPressed("move_right");
await runner.SimulateFrames(5);
var player = runner.Scene<PlayerController>();
AssertFloat(player.Position.X).IsGreater(0.0f);
}
Key SceneRunner methods:
| GDScript | C# | Purpose |
|----------|-----|---------|
| scene_runner(path) | ISceneRunner.Load(path) | Load scene for testing |
| runner.scene() | runner.Scene() | Get the root node |
| await runner.simulate_frames(n) | await runner.SimulateFrames(n) | Advance N frames |
| runner.set_property(name, val) | runner.SetProperty(name, val) | Set node property |
| runner.simulate_key_press(key) | runner.SimulateKeyPress(key) | Simulate key press |
| runner.simulate_action_pressed(action) | runner.SimulateActionPressed(action) | Simulate Input Map action |
| runner.simulate_mouse_move_absolute(pos) | runner.SimulateMouseMoveAbsolute(pos) | Move mouse to position |
| await runner.await_input_processed() | await runner.AwaitInputProcessed() | Wait for input processing |
When to use SceneRunner vs plain auto_free:
- Pure logic (math, data transforms): plain
auto_free(Node.new())+ direct calls — faster, simpler - Needs process callbacks: SceneRunner — process/physics actually run
- Needs input simulation: SceneRunner — only way to simulate keys/mouse/actions in tests
C# test structure
// res://test/csharp/TestMySystem.cs
using GdUnit4;
using static GdUnit4.Assertions;
[TestSuite]
public partial class TestMySystem : TestSuite
{
[TestCase]
public void TestExample()
{
AssertInt(2 + 2).IsEqual(4);
}
[TestCase]
public async Task TestAsync()
{
var node = AutoFree(new MyNode());
AddChild(node);
await AssertSignal(node).WaitUntil(2000).IsEmitted("ready");
}
}
5. Common issues
"No tests found"
- Test file must
extends GdUnitTestSuite - Test methods must start with
test_(GDScript) or have[TestCase]attribute (C#) - File path must be correct — use
res://paths, not absolute OS paths
Test hangs indefinitely
- Missing timeout on
awaitcalls — always usewait_until(seconds)/WaitUntil(ms) - Process callbacks never fire — use SceneRunner with
simulate_frames()instead of rawadd_child() - Infinite loop in the code under test
- Scene tree waiting for a signal that never fires
- Kill the process and check which test was last reported
Scene leaks / orphan nodes
- Always wrap instantiated nodes with
auto_free() - If
before_test()creates nodes,after_test()should clean them up - gdUnit4 reports orphan nodes at the end — treat these as test failures
"Cannot load script" errors
- Check
class_nameconflicts between test and production code - Ensure the test file has valid syntax (run headless-build first)
- For C#: run
dotnet buildbefore
Truncated for display — read the full file on GitHub.
Related Skills
siyuan
46.6kAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
design
130.2kComprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG…
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
