Mutation testing for GDScript and Godot: find the bugs your green tests would miss.
A community tool, not affiliated with or endorsed by the Godot Foundation.
What it is
Coverage tells you a line ran. Mutations tell you if a bug there would be caught: a killed mutant means yes, a survivor means no. A standalone CLI, no AI required.
Same idea, other languages: mutmut for Python, Stryker for JS/TS, PIT for Java.
gdmutant mutates your GDScript (flips >↔>=, and↔or, bumps a number, deletes a statement),
reruns your tests once per change, and reports the survivors.
The --html report, open on turn_order.gd. A survivor in the source, and marked on line 27:
and its detail card: what it means, why it's risky, and how to close it.
Prerequisites
- Godot 4.3+ (see Compatibility for exact versions),
on your PATH so a plain
godot --versionworks, or pass--godot <full-path>on every gdmutant command instead. - GUT or gdUnit4,
already installed and enabled in your project, if you use either. A different test runner works
too, via
--runner command: any headless command that exits non-zero on failure. - Python 3.12+ (check with
python --version).
Quickstart
This mutates corpus/, a small real Godot project bundled in this repo just for this: a real
script and a real GUT/gdUnit4 suite to try gdmutant against before pointing it at your own.
git clone https://github.com/kphutt/gdmutant
cd gdmutant # corpus/ lives right here, at the repo root
pip install . # installs gdmutant and its own dependencies
python scripts/install_gdunit4.py # gdUnit4 is a Godot addon that isn't vendored in git; this fetches it
gdmutant run corpus/turn_order.gd --project corpus --runner gdunit4 --html
# mutates one file, reruns corpus/'s real GdUnit4 tests against each mutant
Output:
...
corpus\turn_order.gd:27 func can_act
27 | return alive and not stunned
| ^ changed and to or: every test still passed
gap Your tests pass whether this needs both sides (`and`) or just one
(`or`). No test covers the case that tells them apart: the
operands disagreeing (one true, one false).
risk Your tests can't tell 'needs both' from 'needs either.' A change
that loosens or tightens this guard would pass every test.
start Add a test where exactly one side is true and the other false,
and assert the outcome.
more https://github.com/kphutt/gdmutant/blob/main/docs/survivors/README.md#boolean
──────────────────────────────────────────────────────────────────────────
Results
Mutation score: 61.1%
killed: 11
timeout: 0 (counted as killed)
survived: 7
ignored: 0 (suppressed, excluded from score)
invalid: 0
error: 0
Wrote HTML report to gdmutant-report-turn_order-<timestamp>.html. Open it in a browser.
Point it at your own project
pip install 'gdmutant==0.1.*' # gdmutant is 0.x: pin the minor so a new one is a move you make on purpose
Pick your test runner with --runner (required). GUT or
gdUnit4 — Godot has no built-in test runner, so both
ship as addons: a plugin folder (addons/gut/ or addons/gdUnit4/) that lives inside a Godot
project. Whichever you pick needs to already be installed there, in the project you're pointing
gdmutant at. Godot itself needs to be on PATH, or point at it with --godot <path>.
gdUnit4's usual test layout matches gdmutant's default --tests res://test, so a gdUnit4 command
needs no --tests flag. GUT needs it spelled out: its stock layout puts suites in test/unit/
instead, and GUT's own -gdir doesn't search subdirectories.
# GUT
gdmutant run ../my-project/src/module.gd --project ../my-project --runner gut --tests res://test/unit --json
# gdUnit4
gdmutant run ../my-project/src/module.gd --project ../my-project --runner gdunit4 --json
--json follows the
mutation-testing-elements schema
See the survivor reference for what
ignored, invalid and error mean.
Killing Survivors
Kill each survivor with a real test, or mark a genuine equivalent with # gdmutant: ignore and a
reason (details). Re-run until nothing survives. A mutation score
isn't a target to hit, it's a direction to watch. There's no universal "good" number.
Compatibility
| Verified at every release | Expected to work | |
|---|---|---|
| Godot | 4.7.0 | 4.3+ |
| Runner | GUT 9.7.1, gdUnit4 6.1.3 | GUT 9.x, gdUnit4 6.x, any headless command |
Documentation
- Survivor reference: every operator explained, the score formula, how to kill or justify each.
- Design & architecture: the engine and the "Saboteur & the Jury" design.
- The gdmutant guide: the CLI, troubleshooting, and the GitHub Action, in one place.
- Contributing · Changelog · Credits
License
MIT, © 2026 kphutt.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gdmutant-0.1.0.tar.gz.
File metadata
- Download URL: gdmutant-0.1.0.tar.gz
- Upload date:
- Size: 507.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f57701e9c4968eb203cd80c88dbf353f5c0e1339c38f007b82c7fdd22545f478
|
|
| MD5 |
5595971608660f6e83bf626dacd5f029
|
|
| BLAKE2b-256 |
871239ad66b01a2f011ac3aabef9767881d9f757c88feda33b29d837d6ced93c
|
Provenance
The following attestation bundles were made for gdmutant-0.1.0.tar.gz:
Publisher:
publish.yml on kphutt/gdmutant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gdmutant-0.1.0.tar.gz -
Subject digest:
f57701e9c4968eb203cd80c88dbf353f5c0e1339c38f007b82c7fdd22545f478 - Sigstore transparency entry: 2342190401
- Sigstore integration time:
-
Permalink:
kphutt/gdmutant@05728864a1c9330d632e2aab2348ff4442f3d61d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kphutt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05728864a1c9330d632e2aab2348ff4442f3d61d -
Trigger Event:
release
-
Statement type:
File details
Details for the file gdmutant-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gdmutant-0.1.0-py3-none-any.whl
- Upload date:
- Size: 133.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d32d3f4db68f1659f88ae74e397003e781b0ef840b4b26d6e6fd15c7f488393
|
|
| MD5 |
d7b9feda70c2f21ee24a0c47bc9675a8
|
|
| BLAKE2b-256 |
34dfc89b88f37e982543c5acc7108bf947018843380e31b84b95b13647f7121c
|
Provenance
The following attestation bundles were made for gdmutant-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on kphutt/gdmutant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gdmutant-0.1.0-py3-none-any.whl -
Subject digest:
0d32d3f4db68f1659f88ae74e397003e781b0ef840b4b26d6e6fd15c7f488393 - Sigstore transparency entry: 2342190406
- Sigstore integration time:
-
Permalink:
kphutt/gdmutant@05728864a1c9330d632e2aab2348ff4442f3d61d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kphutt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05728864a1c9330d632e2aab2348ff4442f3d61d -
Trigger Event:
release
-
Statement type: