Skip to content
Back to skills

Godot Gut Tests

ASecurity

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...

  • 10 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 12, 2026
testinggoc++c#bashnoderailsgitbackend

Works with

  • cli

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned October 12, 2026

npx -y skills add testland/qa --skill godot-gut-tests --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Godot Gut Tests?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Godot Gut Tests
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/testland-godot-gut-tests/badge)](https://www.skillsdirectory.com/skills/testland-godot-gut-tests)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: godot-gut-tests
description: "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."
metadata:
  keywords: "godot, gut, gdscript, unit-test, game-engine, junit-xml, bitwes"
  version: "1.4.17"
---

# godot-gut-tests

## Overview

This skill wraps [GUT (Godot Unit
Test)](https://github.com/bitwes/Gut) - 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):

| Engine | GUT version | Branch |
|---|---|---|
| Godot 4.6.x | 9.x | main |
| Godot 4.7.x | 9.x | godot_4_7 branch |
| Godot 3.4.x | 7.x (currently 7.4.3) | maintained |

Composes with:

- `platform-cert-overview-reference`
  for the canonical six test categories GUT tests map to, and
  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](https://github.com/bitwes/Gut).
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](https://gut.readthedocs.io/), 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_`.

```gdscript
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:

| Hook | Scope |
|---|---|
| `before_all` | Once before every test in the script |
| `before_each` | Before each `test_*` method |
| `after_each` | After each `test_*` method |
| `after_all` | Once after every test in the script |

```gdscript
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](references/assertions.md).

### Inner classes (grouping)

Tests can be organised via inner classes:

```gdscript
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=[...]`:

```gdscript
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:

```gdscript
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:

```gdscript
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:

```bash
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](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](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](references/ci.md). For Godot 3.x projects, swap the engine
version and use GUT 7.x.

## Anti-patterns

| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Forgetting `--headless` in CI | Godot tries to open a window; CI hangs / fails | Always `--headless` for CI runs |
| Forgetting `-gexit` | Godot stays open after the run; CI step never completes | Always `-gexit` for CI runs |
| Test scripts outside `extends GutTest` | Runner skips them silently | Every test script extends `GutTest` (or an inner class that does) |
| Test methods without `test_` prefix | Runner skips them silently | Methods must be prefixed `test_` |
| Using GUT 9 on Godot 3.x (or 7 on 4.x) | Plugin won't load | Match engine + GUT major-version per the compatibility table in this skill |
| Stubbing without a double | `stub(...)` requires a doubled object | Use `double("res://script.gd").new()` first |
| Async tests without `await` | Coroutine completes before assertion | `await` the signal / timer, then assert |
| Asserting `assert_eq` on floats | Floating-point inequality | Use `assert_almost_eq(a, b, tol)` |
| Skipping JUnit XML in CI | CI surfaces no per-test failure detail | Always emit `-gjunit_xml_file=…` and feed to a CI reporter |
| Tests that depend on autoload singletons | Cross-test contamination | Re-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](https://github.com/bitwes/Gut) (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](https://github.com/bitwes/Gut)
  is the most stable entry point.

Files in this skill

  • .tessl-plugin/plugin.json1 KB
  • SKILL.md10.9 KB
  • references/assertions.md1.2 KB
  • references/ci.md2.2 KB
  • references/cli.md1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.