Quickstart
This runs mutastic on samples/pricing, a small Gradle project in mutastic’s repository whose
tests pass and cover every line, and still miss something. It needs the
command line on your PATH.
1. Run it
Section titled “1. Run it”git clone https://github.com/mutastic/mutastic.gitcd mutasticmutastic run samples/pricingmutastic reads the build, runs the tests once to see what each line is reached by, then changes the code one small way at a time and runs the tests that reach each change. One change gets past them:
Missed ────────────────────────────────────────────────────────────
❯ src/main/java/com/example/pricing/Pricing.java 1 missed in 1 method
✗ MISSED no test checks discount() at total == 100 ┌─ src/main/java/com/example/pricing/Pricing.java:17 │ 16 │ public int discount(int total) { 17 │ if (total >= 100) { │ ^^ changed to > 18 │ return total / 10; │ = mutant - if (total >= 100) { + if (total > 100) { = note: every test calls discount() away from the boundary inputs seen: 50, 250 (none is 100) covered by PricingTest#bigOrdersGetTenPercentOff, PricingTest#smallOrdersPayFullPrice (all 2 covering tests still pass) = help: test discount() with total = 100 and assert the result
// in PricingTest @Test void discountWithTotal100() { assertEquals(10, new Pricing().discount(100)); // today 10 · with this mutant 0 } check today's value is the one you intend: the test pins what the code does now
done when mutastic verify m855c8e3c samples/pricing passes reproduce mutastic run --only=m855c8e3c samples/pricing or ignore mutastic ignore m855c8e3c --reason="<why>" samples/pricing
Classes 0 clean | 1 with misses (1) Mutants ✓ 6 caught · ✗ 1 missed · 2 skipped (2 equivalent) (9) Worst at Pricing.discount() 1 ✗ Test runs 8 (21 without coverage selection) Start at 14:39:57 Duration 1.2s (setup 679ms, coverage 189ms, mutants 362ms) Tests 3 on JDK 17 · 4 workers Reports build/mutastic/mutation.json · build/mutastic/mutation.html Score 85.7% threshold 80
PASS 1 missed, above the threshold. Next: mutastic verify <id> samples/pricing · mutastic run --only=<id> samples/pricing · mutastic explain <id> samples/pricing2. Read it
Section titled “2. Read it”- The gap, first: no test calls
discount()with exactly 100, so nothing tells>=from>. - The change that got through, on its line.
- The evidence: the values your tests actually passed (50 and 250), and the tests that reach the line, which all still pass with the change in.
- A test that would catch it, with what the code returns today (10) and with the change (0). Today’s value is what the code does, not necessarily what it should: check it’s what you mean.
- The commands to prove a fix, to see the mutant again, or to record a decision not to test it.
Each names this project, so it works as it’s copied.
m855c8e3cis the mutant’s id.
3. Close the gap
Section titled “3. Close the gap”Add the suggested test to samples/pricing/src/test/java/com/example/pricing/PricingTest.java:
@Testvoid discountWithTotal100() { assertEquals(10, new Pricing().discount(100));}4. Prove it
Section titled “4. Prove it”Run the command the card offered as “done when”:
mutastic verify m855c8e3c samples/pricingmutastic: m855c8e3c is caught by com.example.pricing.PricingTest#discountWithTotal100The mutant is caught: a test now fails when >= becomes >.
5. Run it again
Section titled “5. Run it again”mutastic run samples/pricing Classes 1 clean (1) Mutants ✓ 7 caught · 2 skipped (2 equivalent) (9) Test runs 9 (28 without coverage selection) Start at 14:40:00 Duration 977ms (setup 423ms, coverage 214ms, mutants 340ms) Tests 4 on JDK 17 · 4 workers Reports build/mutastic/mutation.json · build/mutastic/mutation.html Score 100.0% ▲ 14.3 vs last run (code changed) threshold 80
PASS Every mutant was caught.Every mutant is caught, and the score says how far it moved since the last run.
- Results and the score: what caught, missed and the rest mean.
- Operators: the kinds of change mutastic makes.
mutastic run: every option, from the command line, Maven and Gradle.