Skip to main content

gdmutant: banner with Frank the Mutant, the project mascot

Mutation testing for GDScript and Godot: find the bugs your green tests would miss.

CI PyPI Godot 4.3+ GUT 9.x gdUnit4 6.x Python 3.12+ License: MIT

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 >>=, andor, 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:

turn_order.gd:27, return alive and not stunned, with and marked as a survivor

and its detail card: what it means, why it's risky, and how to close it.

The detail card: survived, boolean, turn_order.gd:27, func can_act. Gap: tests pass whether this needs both sides or just one. Risk: a change that loosens or tightens the guard would pass every test. Start: add a test where exactly one side is true and the other false, and assert the outcome

Prerequisites

  • Godot 4.3+ (see Compatibility for exact versions), on your PATH so a plain godot --version works, 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

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

gdmutant-0.1.0.tar.gz (507.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gdmutant-0.1.0-py3-none-any.whl (133.6 kB view details)

Uploaded Python 3

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

Hashes for gdmutant-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f57701e9c4968eb203cd80c88dbf353f5c0e1339c38f007b82c7fdd22545f478
MD5 5595971608660f6e83bf626dacd5f029
BLAKE2b-256 871239ad66b01a2f011ac3aabef9767881d9f757c88feda33b29d837d6ced93c

See more details on using hashes here.

Provenance

The following attestation bundles were made for gdmutant-0.1.0.tar.gz:

Publisher: publish.yml on kphutt/gdmutant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for gdmutant-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d32d3f4db68f1659f88ae74e397003e781b0ef840b4b26d6e6fd15c7f488393
MD5 d7b9feda70c2f21ee24a0c47bc9675a8
BLAKE2b-256 34dfc89b88f37e982543c5acc7108bf947018843380e31b84b95b13647f7121c

See more details on using hashes here.

Provenance

The following attestation bundles were made for gdmutant-0.1.0-py3-none-any.whl:

Publisher: publish.yml on kphutt/gdmutant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page