gdtest

CLI Reference

Every gdtest flag, env var, and subcommand.

Synopsis

gdtest                               # scan the project using discovery globs
gdtest <target>... [options]
gdtest res://test/foo.gd::test_bar   # one test, no worker pool
gdtest init [--no-editor] [--force]
gdtest aggregate <shard.xml>... [--output combined.xml]
gdtest migrate gut|gdunit4 <inputs>... [--out DIR]

A <target> is one of:

  • path/to/file.gd — run every test_* in the file
  • path/to/dir — walk the directory using discovery globs
  • res://path/to/file.gd — same, with project-relative prefix
  • file.gd::test_bar — run exactly that one test function (:: separates the file path from the function name; no worker pool is created, so you get full stderr output directly)

Discovery globs (configurable via the include and exclude settings):

  • test/**/*.gd — any .gd file inside a test/ directory
  • **/*.test.gd — co-located test files anywhere in the project

When walking a directory, gdtest skips hidden folders (.godot, .git…), addons, folders with a .gdignore, and nested Godot projects, as the Godot editor does: an addon's own tests never run as the project's. A directory or file you name directly is always used.

Options

Most options can be set three ways: as a CLI flag, an environment variable, or a configuration key. A flag wins over the env var, which wins over the config file.

Running

FlagEnv varDefaultDescription
-j, --jobs <N>GDTEST_JOBSCPU countParallel worker count (clamped by test count)
--isolation <MODE>GDTEST_ISOLATIONfilefile | test (see below)
--timeout <MS>GDTEST_TIMEOUT5000Per-test timeout; a test that exceeds it is an error and its worker restarts
--timeout-warn <MS>GDTEST_TIMEOUT_WARNoffWarn about slow tests without stopping them; must be below --timeout
--fail-fastGDTEST_FAIL_FASToffStop after the first failure or error
--retries <N>GDTEST_RETRIES0Re-run a failing test up to N more times; timeouts and crashes are not retried
--profile <NAME>——Activate a [profile.NAME] block from the config
--watch—offRe-run the targets after every .gd change; Ctrl-C to exit
--no-rewrite—offRun assert(a == b) as written instead of rewriting it for rich diffs
--update-snapshots—offAccept new or changed snapshot values
--seed <N>GDTEST_SEEDrandomFix the RNG seed of forall property tests
--bench—offRun only bench_* functions (see Running Tests)

Selecting tests

FlagEnv varDescription
--filter <REGEX>—Only tests whose file :: func matches the regex
--tag <TAG>GDTEST_INCLUDE_TAGSOnly tests with this tag (repeatable, any of them)
--exclude-tag <TAG>GDTEST_EXCLUDE_TAGSSkip tests with this tag (repeatable)
--changed-only—Only tests affected by uncommitted changes (git diff HEAD)
--changed-since <REF>—Only tests affected by changes since a git ref, e.g. origin/main
--partition <SPEC>—Run one shard of the suite (see below)
--list—Print the discovered tests as file :: func and exit

Output

FlagEnv varDefaultDescription
--reporter <KIND>GDTEST_REPORTERprettypretty | tap | json | junit
--output-dir <DIR>——Write JSON events as numbered files under DIR (used by the editor panel)
--html-report [PATH]—gdtest-report.htmlWrite a standalone HTML report after the run
--coverage—offInstrument sources and write coverage/lcov.info
--min-coverage <PCT>——Fail when total line coverage is below PCT; needs --coverage
--coverage-exclude <GLOB>GDTEST_COVERAGE_EXCLUDE—Leave matching files out of coverage (repeatable)
--mutate <FILE>——Mutation testing of one source file

Godot

FlagEnv varDefaultDescription
--godot-exe <PATH>GODOT_EXEauto-discoveredGodot 4 binary
--display-mode <MODE>GDTEST_DISPLAY_MODEheadlessheadless | windowed
--project-path <DIR>—.Godot project root
--harness <PATH>GDTEST_HARNESSaddons/gdtest/gdtest_worker.gdWorker script
--no-auto-import—offSkip the one-time godot --headless --import that runs when the class cache is missing

Reporters

pretty (default) — coloured human-readable output with a spinner and summary line.

tap — TAP v14; works with tap-junit, tap-parser and most CI TAP consumers.

json — one JSON event per line, for tools that read results as they arrive.

junit — JUnit XML on stdout. Redirect it to a file for GitHub Actions or GitLab CI test reports.

gdtest test/ --reporter junit > results.xml

Isolation modes

file (default) — a worker stays alive across the tests of a file, and across files. Fast; tests that run in the same worker share autoload state.

test — the worker restarts between every test. About 200 ms more per test, but each test starts with clean autoloads and Engine metadata. Use it when tests change global singletons and those changes leak.

Instead of switching the whole run, mark only the files or tests that need it:

## @isolation test
extends GdTest            # every test in this file gets its own worker

## @isolation file
func test_fast_path() -> void:   # except this one
    pass

Partitioning (CI sharding)

Split the suite across CI jobs with --partition, then merge the JUnit files:

# Round-robin: each job gets every 3rd test
gdtest test/ --partition count:1/3 --reporter junit > shard-1.xml
gdtest test/ --partition count:2/3 --reporter junit > shard-2.xml
gdtest test/ --partition count:3/3 --reporter junit > shard-3.xml

# Hash-based: the same test always lands on the same shard
gdtest test/ --partition hash:1/3

# After all shards
gdtest aggregate shard-*.xml --output combined.xml

count:N/M takes every M-th test starting at position N−1. hash:N/M uses an FNV-1a hash of script::func, so a test keeps its shard as the suite grows.

Subcommands

gdtest init

gdtest init
gdtest init --no-editor
gdtest init --project-path /path/to/game
gdtest init --force

Installs the bundled addons/gdtest/ runtime and the addons/gdtest_editor/ panel into the project. --no-editor installs only the runtime.

If a file already exists with different contents, init stops rather than overwrite local changes. --force replaces the project copy with the version bundled in the current gdtest binary.

gdtest aggregate

gdtest aggregate shard-1.xml shard-2.xml --output combined.xml

Merges JUnit XML files written by --reporter junit into one report, on stdout unless --output is given.

gdtest migrate

gdtest migrate gut test/
gdtest migrate gdunit4 test/ --out .gdtest_rewrite/gdunit4

Writes gdtest versions of GUT or gdUnit4 tests next to the originals, which are left untouched, plus a report of what could not be converted. Output goes to .gdtest_rewrite/<framework> unless --out is given. See Migrating from GUT and Migrating from gdUnit4.

Single-test mode

gdtest res://test/test_player.gd::test_jump   # bypasses the pool; full stderr visible