Testland
Browse all skills & agents

godot-gut-tests

Author and run GUT (Godot Unit Test) - the community-canonical GDScript test framework at github.com/bitwes/Gut and gut.readthedocs.io. Covers install (Godot Asset Library or manual `addons/gut/` copy + plugin enable), GUT panel inside the editor, writing tests that extend GutTest with `test_` prefix methods, the assertion family (assert_eq / assert_almost_eq / assert_true / assert_signal_emitted), lifecycle hooks (before_each / after_each / before_all / after_all), inner classes for grouping, parameterized tests via `params=[...]`, doubles / stubs / spies, async / coroutine tests, the command-line runner (`-d -s addons/gut/gut_cmdln.gd -gdir=res://test -gjunit_xml_file=... -gexit`), JUnit XML export, and CI integration. Godot 4.x uses GUT 9.x (current main branch supports 4.6.x; godot_4_7 branch for 4.7.x); Godot 3.x uses GUT 7.x. Use when the unit under test is GDScript code in a Godot project.

Install with skills.sh (any agent)

npx skills add testland/qa --skill godot-gut-tests
View source

godot-gut-tests

Overview

This skill wraps GUT (Godot Unit Test) (opens in new window) - the community-canonical GDScript test framework. Godot does not ship a first-party equivalent of Unity's Test Framework or Unreal's Automation System for GDScript user code.

Compatibility (per the GUT README):

EngineGUT versionBranch
Godot 4.6.x9.xmain
Godot 4.7.x9.xgodot_4_7 branch
Godot 3.4.x7.x (currently 7.4.3)maintained

Composes with:

  • game-test-categories-reference for the canonical six categories GUT tests map to.
  • platform-cert-overview-reference for cert-gated requirements GUT tests should cover where the title ships to Xbox / PlayStation / Switch via Godot exports.
  • gameplay-recording-replay for replay-driven coverage authored on top of GUT.

When to use

  • Unit under test is GDScript code in a Godot project (Godot 3.x or 4.x).
  • You want a before_each / after_each / parameterised / mock test surface inside the editor with a CLI runner for CI.
  • You need JUnit XML export so a generic CI dashboard (GitHub Actions test reporter, Jenkins JUnit plugin, etc.) can surface failures.

For pure-C# tests inside Godot's C# scripting layer, use .NET-canonical test runners (xUnit, NUnit) rather than GUT - GUT is GDScript-first.

Authoring

Install

Two paths:

Asset Library (recommended for compatible engine versions):

  1. Inside the Godot editor → AssetLib tab.
  2. Search for "GUT" → install.
  3. Project → Project Settings → Plugins → enable GUT.
  4. Re-launch the editor.

Manual:

  1. Clone or download the GUT repo from github.com/bitwes/Gut (opens in new window).
  2. Copy the addons/gut/ directory into your project's addons/ directory.
  3. Enable the plugin as above.

After enabling, a GUT panel appears in the bottom dock.

Project layout

Typical convention (the GUT runner defaults work with this):

my_godot_project/
  addons/gut/                # the framework
  src/
    health.gd
    enemy_ai.gd
  test/
    unit/
      test_health.gd
      test_enemy_ai.gd
    integration/
      test_save_load.gd
  .gutconfig.json            # optional config file
  project.godot

Minimal test

Per gut.readthedocs.io (opens in new window), tests "extend the GutTest class and use assertion methods like assert_eq, assert_almost_eq, assert_true, and assert_signal_emitted". Test methods are prefixed test_.

extends GutTest

func test_damage_deducts_correct_amount():
    var health := preload("res://src/health.gd").new()
    health.initialize(100)

    health.apply_damage(35)

    assert_eq(health.current, 65, "65 HP after 35 dmg from 100")

Lifecycle hooks

Lifecycle hooks:

HookScope
before_allOnce before every test in the script
before_eachBefore each test_* method
after_eachAfter each test_* method
after_allOnce after every test in the script
extends GutTest

var _player

func before_each():
    _player = preload("res://src/player.gd").new()

func after_each():
    _player.queue_free()
    _player = null

func test_player_starts_with_full_health():
    assert_eq(_player.health, _player.max_health)

Assertion family

Tests extend GutTest and call assertion methods; the most common are assert_eq / assert_ne, assert_almost_eq (floats), assert_true / assert_false, assert_null / assert_not_null, assert_signal_emitted, and assert_called (spy). The full assertion table is in references/assertions.md.

Inner classes (grouping)

Tests can be organised via inner classes:

extends GutTest

class TestAddItem:
    extends GutTest

    var _inv

    func before_each():
        _inv = preload("res://src/inventory.gd").new()

    func test_increases_count_by_stack():
        _inv.add_item("potion", 3)
        assert_eq(_inv.count_of("potion"), 3)

    func test_rejects_over_max_stack():
        var ok = _inv.add_item("potion", 999)
        assert_false(ok)

class TestRemoveItem:
    extends GutTest
    # …

Each inner class reports as its own grouping in the GUT panel.

Parameterised tests

GUT supports parameterized tests via params=[...]:

extends GutTest

var damage_cases = [
    [100, 25, 75],
    [100, 100, 0],
    [50,  60,  0],     # clamps to zero, not negative
    [100, 0,  100],
]

func test_apply_damage_table(params=use_parameters(damage_cases)):
    var health = preload("res://src/health.gd").new()
    health.initialize(params[0])
    health.apply_damage(params[1])
    assert_eq(health.current, params[2])

Each row in damage_cases becomes its own test case in the report.

Doubles, stubs, and spies

GUT provides doubling (full and partial), stubbing, and spies. Typical pattern:

extends GutTest

func test_save_calls_backend():
    var backend_double = double("res://src/backend.gd").new()
    stub(backend_double, "save").to_return(true)

    var manager = preload("res://src/save_manager.gd").new()
    manager.backend = backend_double

    manager.save_game({"hp": 50})

    assert_called(backend_double, "save", [{"hp": 50}])

double(path) produces a fake script; stub(...).to_return(value) configures return values; assert_called(...) is the spy assertion.

Async / coroutine tests

GUT supports coroutines and await in tests - a test_* method can await signals or timers and the runner waits before moving on:

extends GutTest

func test_async_load_completes():
    var loader = preload("res://src/async_loader.gd").new()
    loader.start_load("res://big_scene.tscn")

    await get_tree().create_timer(0.5).timeout

    assert_true(loader.is_done)
    assert_not_null(loader.result)

Running

From the GUT editor panel

After enabling the plugin, the GUT panel appears in the bottom dock (Editor → Bottom Panel → GUT) with normal and compact views. Click Run All or right-click a script → Run to execute.

From the command line

The command-line runner is invoked via -d -s addons/gut/gut_cmdln.gd plus GUT-specific options:

godot \
  --headless \
  -d \
  -s addons/gut/gut_cmdln.gd \
  -gdir=res://test \
  -gjunit_xml_file=artifacts/gut-junit.xml \
  -gexit

--headless runs without a display window (required for CI) and -d runs in debug mode so the runner script (addons/gut/gut_cmdln.gd) executes. The full CLI flag table and the .gutconfig.json config schema are in references/cli.md.

Parsing results

Set -gjunit_xml_file=… to export JUnit XML - the recommended CI-consumable output, in the standard schema that GitHub Actions test reporters, the Jenkins JUnit plugin, and the GitLab CI test report widget consume. GUT also tracks pre-test errors / orphan nodes / unhandled signals, which surface in the panel and the report. The full XML shape is in references/ci.md.

CI integration

Run the headless CLI command in a CI job, upload the JUnit XML as an artifact, and feed it to a JUnit-aware reporter. A complete GitHub Actions workflow (Godot download, GUT run, artifact upload, dorny/test-reporter) is in references/ci.md. For Godot 3.x projects, swap the engine version and use GUT 7.x.

Anti-patterns

Anti-patternWhy it failsFix
Forgetting --headless in CIGodot tries to open a window; CI hangs / failsAlways --headless for CI runs
Forgetting -gexitGodot stays open after the run; CI step never completesAlways -gexit for CI runs
Test scripts outside extends GutTestRunner skips them silentlyEvery test script extends GutTest (or an inner class that does)
Test methods without test_ prefixRunner skips them silentlyMethods must be prefixed test_
Using GUT 9 on Godot 3.x (or 7 on 4.x)Plugin won't loadMatch engine + GUT major-version per the compatibility table in this skill
Stubbing without a doublestub(...) requires a doubled objectUse double("res://script.gd").new() first
Async tests without awaitCoroutine completes before assertionawait the signal / timer, then assert
Asserting assert_eq on floatsFloating-point inequalityUse assert_almost_eq(a, b, tol)
Skipping JUnit XML in CICI surfaces no per-test failure detailAlways emit -gjunit_xml_file=… and feed to a CI reporter
Tests that depend on autoload singletonsCross-test contaminationRe-initialise / reset autoloads in before_each

Limitations

  • No official Godot test framework. Unlike Unity (UTF) or Unreal (Automation), Godot does not ship a first-party GDScript test framework - GUT is community-maintained at github.com/bitwes/Gut (opens in new window) (MIT license). For platform-cert evidence trails, vendor / partner reviewers may ask for the framework's provenance.
  • GDScript-only. C# Godot projects should use .NET test runners (xUnit / NUnit) - GUT is GDScript-first.
  • Godot version coupling. Bumping the engine usually bumps GUT - match the engine + GUT major version per the compatibility table in the Overview.
  • Doubles depend on script paths. double("res://path.gd") needs the script's res:// path; doubling autoloads or engine C++ classes is not supported directly.
  • No common exit-code definition for engine failures. A Godot crash mid-run gives exit 0 in some configurations - parse the JUnit XML for failures>0 as the source of truth.
  • GUT panel inside editor is the canonical UX. Running tests via --headless CI works but is slower per-test than the in-editor panel because Godot starts fresh each run.
  • gut.readthedocs.io page churn. Direct deep links (e.g., a Configuration page) sometimes return 404 between releases - the project README on github.com (opens in new window) is the most stable entry point.

GUT assertion family - reference

View source (opens in new window)

GUT assertion family - reference

Common assertions for godot-gut-tests, per gut.readthedocs.io (opens in new window) and the GUT README (opens in new window). The README notes GUT exposes "a plethora of asserts and utility methods" - check the addons/gut/test.gd source in your installed version for the complete signature list at the engine version you ship against.

AssertionUse
assert_eq(a, b, msg)Equality
assert_ne(a, b, msg)Inequality
assert_almost_eq(a, b, tol, msg)Float comparison within tolerance
assert_true(v, msg) / assert_false(v, msg)Boolean
assert_null(v, msg) / assert_not_null(v, msg)Null check
assert_has(coll, v, msg) / assert_does_not_have(coll, v, msg)Membership
assert_signal_emitted(obj, "signal_name", msg)Signal emission
assert_signal_emitted_with_parameters(obj, "name", args, msg)Signal emission with payload
assert_gt(a, b, msg) / assert_lt(a, b, msg)Ordering
assert_called(double, "method_name", args)Spy verification

Use assert_almost_eq(a, b, tol) for floats - assert_eq fails on floating-point inequality.

GUT results parsing and CI - reference

View source (opens in new window)

GUT results parsing and CI - reference

JUnit XML output and CI wiring for godot-gut-tests.

JUnit XML shape

GUT exports JUnit XML when -gjunit_xml_file=... is set - the recommended CI-consumable output per gut.readthedocs.io (opens in new window). Top-level shape:

<testsuites name="GUT" tests="42" failures="1" disabled="0" errors="0" time="3.214">
  <testsuite name="res://test/unit/test_health.gd"
             tests="5" failures="1" errors="0" time="0.124">
    <testcase classname="test_health"
              name="test_damage_deducts_correct_amount"
              time="0.012"/>
    <testcase classname="test_health"
              name="test_clamps_below_zero"
              time="0.014">
      <failure message="Expected 0 but was -5"
               type="AssertionFailed"/>
    </testcase>
  </testsuite>
</testsuites>

Standard JUnit XML - consumed by GitHub Actions test reporters, Jenkins JUnit plugin, GitLab CI test report widget, etc. Per the GUT README (opens in new window), GUT also tracks pre-test errors / orphan nodes / unhandled signals - these surface in the GUT panel and the JUnit report.

GitHub Actions

jobs:
  gut-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Godot
        run: |
          GODOT=4.6.1-stable
          wget -q https://github.com/godotengine/godot/releases/download/${GODOT}/Godot_v${GODOT}_linux.x86_64.zip
          unzip -q Godot_v${GODOT}_linux.x86_64.zip -d godot
          mv "godot/Godot_v${GODOT}_linux.x86_64" godot/godot
          chmod +x godot/godot
      - name: Run GUT
        run: |
          mkdir -p artifacts
          ./godot/godot --headless -d \
            -s addons/gut/gut_cmdln.gd \
            -gdir=res://test \
            -gjunit_xml_file=artifacts/gut-junit.xml \
            -gexit
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: gut-junit
          path: artifacts/gut-junit.xml
      - uses: dorny/test-reporter@v1
        if: always()
        with:
          name: GUT
          path: artifacts/gut-junit.xml
          reporter: java-junit

For Godot 3.x projects, swap the engine version and use GUT 7.x.

GUT command-line runner - flag and config reference

View source (opens in new window)

GUT command-line runner - flag and config reference

Full flag list and config schema for godot-gut-tests. The CLI runner is invoked via -d -s addons/gut/gut_cmdln.gd plus GUT-specific options, per gut.readthedocs.io (opens in new window).

CLI flags

FlagEffect
-gdir=res://testRecurse this directory for tests
-gtest=res://test/unit/test_health.gdRun a single test script
-ginner_class=TestAddItemLimit to one inner class
-gunit_test_name=test_increases_count_by_stackLimit to one test method
-gconfig=res://.gutconfig.jsonLoad config from JSON
-gjunit_xml_file=artifacts/gut-junit.xmlWrite JUnit XML report
-gjunit_xml_timestampAdd timestamp suffix to filename
-glog=3Log verbosity (0 - 3)
-gexitExit Godot after run (essential in CI)

--headless runs Godot without a display window - required for most CI environments. -d runs in debug mode so the test runner script (addons/gut/gut_cmdln.gd) executes.

Config file

A .gutconfig.json at the project root lets the GUT panel and CLI runner share settings:

{
  "dirs": ["res://test/unit", "res://test/integration"],
  "include_subdirs": true,
  "log_level": 1,
  "junit_xml_file": "artifacts/gut-junit.xml",
  "double_strategy": "partial"
}

Field names per gut.readthedocs.io (opens in new window) - check your installed addons/gut/ version for the authoritative schema.

Related skills

game-perf-profiling

Profiles game builds against frame-time, memory, GPU draw-call, and GC-spike budgets using Unity Profiler + Profile Analyzer + Performance Testing package and Unreal Insights + stat commands. Establishes pass/fail thresholds (16.6 ms at 60 fps, 33.3 ms at 30 fps), writes automated performance regression tests that run in CI, and emits a structured budget report per SKU. Use when a title must hit a declared frame-time or memory budget before a milestone gate or platform-cert submission, or when a recent change needs a performance regression check.

game-test-categories-reference

Pure-reference catalog of the testing categories that apply to a video-game build before it ships. Defines the six canonical buckets the industry tests against - functional / compliance / compatibility / performance / localization / accessibility - plus the multiplayer and content-rating sub-axes. Cross-references each bucket to the platform-holder vocabulary that drives it (Microsoft Xbox Requirements / XR test cases, Sony TRC, Nintendo Lotcheck, Steam Direct review). Use as the taxonomy lookup when planning a game test pass, scoping QA effort, mapping platform-cert findings back to internal test categories, preparing a submission checklist, reviewing first-party certification requirements, or triaging cert testing failures against internal categories.

gameplay-recording-replay

Build a deterministic gameplay record/replay test artefact for Unity, Unreal, or Godot - record a player session, save it to disk, replay it bit-for-bit, and assert that the resulting game state matches the original. Covers Unity Input System's InputEventTrace API (Enable / Disable / WriteTo / ReadFrom / Replay) for input-level capture, Unreal's Replay System (DemoRec / DemoPlay / DemoStop console commands plus DemoNetDriver + NetworkReplayStreamer, default storage at %LOCALAPPDATA%/{Project}/Saved/Demos) for replication-stream capture, and Godot's community-pattern deterministic-RNG + input-script replay since Godot ships no first-party replay system. Use when authoring a regression-test artefact for player-recorded sessions, building a netcode replay for spectator / esports, or producing reproducible bug repros for cert teams.

multiplayer-state-machine-coverage

Build a coverage matrix for a networked-game state machine that exercises connect / authority-handoff / disconnect / reconnect / host-migration paths across Unity Netcode for GameObjects, Unreal Engine replication, and Mirror Networking. Workflow: enumerate the engine's connection states + ownership states + replicated-property update rules, cross them against latency / loss / out-of-order packet injection, encode each combination as a test fixture, and emit a go / no-go gate. Use before submitting a multiplayer title to platform cert - Microsoft's cert guide lists 'Multiplayer does not work as expected' as one of the most common Hold reasons, and Xbox XR-067 (MPSD session state) is failed by uncovered state-machine paths.

platform-cert-overview-reference

Pure-reference catalog of the four platform-holder certification regimes a multi-platform title submits to before release: Microsoft Xbox Requirements (XR) / Xbox certification on learn.microsoft.com, Sony Technical Requirements Checklist (TRC) on the gated PlayStation DevNet portal, Nintendo Lotcheck on the gated Nintendo Developer Portal, and Steam Direct review on partner.steamgames.com. Documents the submission workflow, severity / pass-fail vocabulary, test-bench configurations, and known SLAs for each platform. Cites public sources inline; cites gated NDA portals by stable ID per PLUGIN_AUTHORING.md Step 4 fallback. Use when planning a cert calendar, mapping internal QA findings to the platform's vocabulary, or sequencing submissions across platforms.

unity-test-framework

Author and run the Unity game-engine Test Framework (`com.unity.test-framework`, currently v1.8). Distinct from the ThrowTheSwitch Unity C testing library at throwtheswitch.org/unity - the two tools share only a name. Covers package install via Package Manager, the EditMode vs PlayMode split, the [Test] / [UnityTest] / [SetUp] / [TearDown] / [UnityPlatform] attributes, assembly-definition setup (Editor folder vs asmdef with `includePlatforms` / `optionalUnityReferences: [TestAssemblies]`), Test Runner window, command-line batch invocation with `-runTests` / `-testPlatform` / `-testResults` / `-testFilter` / `-testCategory`, NUnit 3.5 assertion API, and CI integration. Use when the unit under test is C# Unity code that needs to exercise the Unity runtime or editor.

unreal-automation-system

Author and run Unreal Engine's Automation Test Framework - Epic's C++ test framework for UE 4.x / 5.x, documented at dev.epicgames.com/documentation/en-us/unreal-engine. Covers the five test categories Epic defines (Unit / Feature / Smoke / Content Stress / Screenshot Comparison), the IMPLEMENT_SIMPLE_AUTOMATION_TEST and IMPLEMENT_COMPLEX_AUTOMATION_TEST macros, the BDD-style Automation Spec API (DEFINE_SPEC / BEGIN_DEFINE_SPEC / Describe / It / BeforeEach / LatentIt / xIt), latent commands (ADD_LATENT_AUTOMATION_COMMAND), the Automation Driver for UI input simulation (IAutomationDriverModule::Get().CreateDriver(), By::Id / By::Path locators), running via Session Frontend (Window > Test Automation) and command line (-ExecCmds="Automation RunTests …"), and CI integration. Use when the unit under test is C++ Unreal code that needs the UE runtime, editor, or UMG UI surface.