Skip to main content

moonbuggy

Fast, agent-first mutation testing for Python.

Mutation testing measures whether your tests would actually notice if the code broke. It makes small changes to your source — flipping a < to a <=, a True to a False — and reruns the tests. A change no test objects to is a gap: a missing test, a weak assertion, or a line nothing exercises.

Two things make moonbuggy different:

  • Speed. It runs only the tests that actually cover each mutated line, applies mutations in memory rather than writing files, runs mutants in parallel forked processes, does the work every mutant shares once in a warm host rather than once per mutant, and caches results across runs. On a suite where test execution dominates, that is 38x faster than the naive approach of rerunning everything per mutant, and about 1.9x faster than mutmut. Both figures come from make bench, which measures all three tools in one session on one machine; see benchmark results for what they do and do not establish.
  • Output built for agents. Results are JSON Lines, with a derived plaintext view whose every line starts with a fixed keyword, so grep SURVIVED works with no knowledge of the schema.

Install

Full documentation is published at jhamon.github.io/moonbuggy.

Requires Python 3.12+ and pytest.

pip install .

Use

From your project root, with no flags and no configuration:

moonbuggy

It discovers your source layout, runs one instrumented pass to build a line→test map, then runs each mutant against only its covering tests.

Two files land in .moonbuggy/:

file role
results.jsonl canonical, one JSON object per mutant
results.txt plaintext view, derived from the JSONL

Exit code is 0 when nothing survived, 1 when there are survivors, and 2 when the run could not start.

Reading the output

Every plaintext line begins with one of exactly five keywords:

keyword meaning
KILLED a test failed under the mutation — the good outcome
SURVIVED every selected test passed — a gap, or an equivalent mutant
TIMEOUT the mutation caused a hang, killed by the time budget
SUSPICIOUS pytest could not complete; needs a look
SKIPPED suppressed, or filtered out by configuration

So the thing you usually want is:

grep SURVIVED .moonbuggy/results.txt

Each line carries key=value tokens, including nearest_test= — the test to extend to close that gap.

Lines are one per mutant and never contain the diff, so they stay grep- and awk-friendly. To see a mutant in full:

moonbuggy show 'shipping.py:5:comparison_swap:0'

Suppressing an equivalent mutant

Some mutants cannot be killed by any test because the mutated program is genuinely equivalent — changing a cache size, say. Detecting these automatically is undecidable, so moonbuggy does not try. Mark them in the source:

CACHE_SIZE = 128  # moonbuggy: skip -- tuning only, no observable behaviour

They are then reported SKIPPED rather than silently dropped, so the mutant count stays honest.

Options

Nothing below is required.

--timeout SECONDS    before a mutant is called TIMEOUT (default: 30)
--operators NAMES    comma-separated subset, e.g. comparison_swap,boundary
--include FRAGMENT   only mutate paths containing FRAGMENT (repeatable)
--exclude FRAGMENT   skip paths containing FRAGMENT (repeatable)
--jobs N             mutants to run concurrently (default: CPU count - 1)
-n, --workers N      pytest-xdist workers per mutant run
--source DIR         directory to mutate, if discovery guesses wrong
--no-cache           ignore and do not update the cache
--clear-cache        delete the cache, then run
--quiet              summary line only

Development

python -m venv .venv && .venv/bin/pip install -e '.[dev,bench]'
command what it checks
make test fast unit suite
make check-oracle every mutant against the hand-written oracle
make check-spike in-memory mutation, assert rewriting, xdist
make check-mutmut advisory cross-check of the oracle against mutmut
make bench moonbuggy vs mutmut vs naive
make check-fresh-install clean install, zero-config run
make check-all all of the above

The project under tests/fixtures/sample_project is input data, not tests of moonbuggy — a small pytest project whose 22 mutants have hand-written expected outcomes in oracle.toml. Some of its tests hang or fail by design once mutated, which is why the outer suite excludes it.

Status

Phase 0 and Phase 1 of the acceptance criteria are implemented and all criteria are met. Speed numbers and the four measured iterations behind them are in docs/benchmark-results.md.

Design notes: spike A (in-memory mutation, xdist), spike B (coverage mechanism).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

moonbuggy-0.1.1.tar.gz (296.9 kB view details)

Uploaded Source

Built Distribution

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

moonbuggy-0.1.1-py3-none-any.whl (79.6 kB view details)

Uploaded Python 3

File details

Details for the file moonbuggy-0.1.1.tar.gz.

File metadata

  • Download URL: moonbuggy-0.1.1.tar.gz
  • Upload date:
  • Size: 296.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for moonbuggy-0.1.1.tar.gz
Algorithm Hash digest
SHA256 204c2417971b83c54d236e69055b82c8d75cab13b78321d87a5dac2db6a50e9f
MD5 ca19c626b58d6be5c4b29aa06b879f1e
BLAKE2b-256 390b1db44169c6ba523a52ab4b3a93d1fc3e91799ff5dad532c6789bfe28aa39

See more details on using hashes here.

Provenance

The following attestation bundles were made for moonbuggy-0.1.1.tar.gz:

Publisher: release.yml on jhamon/moonbuggy

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

File details

Details for the file moonbuggy-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: moonbuggy-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 79.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for moonbuggy-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 dde2378f31519987555989141b158ebbc7b8a599f86c7fc4640be82c27e4a27b
MD5 5ba23b99137cc943ea4040f4664b012e
BLAKE2b-256 1f8c1481056865c445f5984f53d4c9fda6d831e29aabb5cd150c573a661fa45a

See more details on using hashes here.

Provenance

The following attestation bundles were made for moonbuggy-0.1.1-py3-none-any.whl:

Publisher: release.yml on jhamon/moonbuggy

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

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page