More screenshots

    Works with the AI Agents you already use

    Claude CodeClaude CodeCursorCursorCodex CLICodex CLIMuseMuseDotsDots+22 more

    Mutation-Verified Test Generator (Jest, Vitest, pytest)

    1

    Writes tests for your code, then proves they catch bugs with a mutation check, and strengthens them until they do.

    $12

    Secure checkout via Stripe

    Money-back guarantee

    Converts to your local currency at checkout

    Security scanned
    Mutation-Verified Test Generator (Jest, Vitest, pytest)

    Mutation-Verified Test Generator (Jest, Vitest, pytest)

    Example session with this skill installed

    Write tests for billing/invoice.py and check that they would actually catch bugs.Write tests for billing/invoice.py and check that they would actually catch bugs.

    • Read your context and instructions
    • Compiled the mutation-verified test generator

    Test plan: billing/invoice.py (whole module)

    Framework: pytest · Test file: tests/test_invoice.py (extending existing)
    Style: mirror tree, flat test_ functions, plain assert, no mocks

    #BehaviourKindInput → expected
    1Commas and $ strippedhappy path"1,234.50" → 1234.50; "$12" → 12.00
    2Whitespace trimmed, quantized to centshappy path" 7.5 " → Decimal("7.50") (str checked)
    3Half-up roundingboundary"0.005" → 0.01; "0.004" → 0.00; "2.675" → 2.68
    4Zero acceptedboundary"0" → 0.00
    5Empty / blank / bare $error path"", " ", "$" → ValueError "empty amount"
    6Junkerror path"abc", "1.2.3" → ValueError "not a number"
    7Negativeerror path"-5", "$-0.01" → ValueError "negative"
    8No fee within graceboundarydays 0, 5 → 0.00
    9Fee starts day 6boundary100 @ 6 days → 1.00, @ 10 → 5.00
    10Cap at 25%boundary100 @ 29 → 24.00, @ 30 → 25.00, @ 31/100 → 25.00
    11Fee roundingboundary0.50 @ 6 days → 0.01
    12Discount tiersboundary1, 19 → 0; 20, 99 → 0.05; 100, 500 → 0.10
    13Total with VAThappy path10×2 → 24.00; 10×1 → 12.00
    14Tax exempthappy path10×2 exempt → 20.00
    15Discounts in totalboundary10×19 → 228.00; 10×20 → 228.00; 10×100 → 1080.00
    16Late fee in total (on VAT-inclusive net)happy path10×2, 10 days → 25.20; exempt → 21.00
    17Total roundingboundary0.335×1 exempt → 0.34
    18Invalid unitserror path / boundaryunits 0, -1 → ValueError; units 1 accepted

    Mocking: nothing, since these are pure functions.
    Not covered: none.

    Possible source issue to check: "NaN" or "Infinity" passed to parse_amount may raise decimal.InvalidOperation instead of ValueError. I'll check this rather than assume it.

    Next I'm scoring the existing test as it stands:


    ▸ Tool output: mutation check

    $ python3 ~/.claude/skills/mutation-verified-test-generator/scripts/mutate.py --target billing/invoice.py --max-mutants 20 -- .venv/bin/python -m pytest tests/test_invoice.py -q -p no:cacheprovider
    Mutants: 29 candidates, 20 selected (cap 20).
    Baseline: running tests on the unmodified code…
    Baseline passed in 0.2s. Timeout per mutant: 30s.
    Backups: /var/folders/b1/327sn4cn0t1fb223h0pdsn140000gn/T/mvtg-backups (removed after a verified restore; if this process is killed, run again with --restore).
    [ 1/20] done
    [ 2/20] done
    [ 3/20] done
    [ 4/20] done
    [ 5/20] done
    [ 6/20] done
    [ 7/20] done
    [ 8/20] done
    [ 9/20] done
    [10/20] done
    [11/20] done
    [12/20] done
    [13/20] done
    [14/20] done
    [15/20] done
    [16/20] done
    [17/20] done
    [18/20] done
    [19/20] done
    [20/20] done
    
    Mutation score: 9/20 (45.0%)
      killed              9
      killed (timeout)    0
      survived           11
      (re-run with --show-mutants to see them, once the new tests are written)
    
    Restore check:
      OK    billing/invoice.py  sha256 and modification time match original
    

    The existing test scores 45% (9/20). Before writing tests I'll check the NaN/Infinity suspicion:


    ▸ Write tests/test_invoice.py


    ▸ Tool output: tests

    $ .venv/bin/python -m pytest tests/test_invoice.py -q -p no:cacheprovider
    ....................................................                     [100%]
    52 passed in 0.03s
    

    All 52 pass on the unmodified code. Running the first-draft mutation check:


    ▸ Tool output: mutation check

    $ python3 ~/.claude/skills/mutation-verified-test-generator/scripts/mutate.py --target billing/invoice.py --max-mutants 20 --show-mutants -- .venv/bin/python -m pytest tests/test_invoice.py -q -p no:cacheprovider
    Mutants: 29 candidates, 20 selected (cap 20).
    Baseline: running tests on the unmodified code…
    Baseline passed in 0.1s. Timeout per mutant: 30s.
    Backups: /var/folders/b1/327sn4cn0t1fb223h0pdsn140000gn/T/mvtg-backups (removed after a verified restore; if this process is killed, run again with --restore).
    [ 1/20] M1   killed           billing/invoice.py:20  boundary     if value < 0:  ->  if value <= 0:
    [ 2/20] M2   killed           billing/invoice.py:20  negate       if value < 0:  ->  if value >= 0:
    [ 3/20] M3   killed           billing/invoice.py:22  return       return value.quantize(CENT, rounding=ROUND_HALF_UP)  ->  return None
    [ 4/20] M4   survived         billing/invoice.py:27  boundary     if days_late <= GRACE_DAYS:  ->  if days_late < GRACE_DAYS:
    [ 5/20] M5   killed           billing/invoice.py:27  negate       if days_late <= GRACE_DAYS:  ->  if days_late > GRACE_DAYS:
    [ 6/20] M6   killed           billing/invoice.py:28  return       return Decimal("0.00")  ->  return None
    [ 7/20] M7   killed           billing/invoice.py:29  arithmetic   rate = (days_late - GRACE_DAYS) * DAILY_LATE_RATE  ->  rate = (days_late + GRACE_DAYS) * DAILY_LATE_RATE
    [ 8/20] M8   killed           billing/invoice.py:29  arithmetic   rate = (days_late - GRACE_DAYS) * DAILY_LATE_RATE  ->  rate = (days_late - GRACE_DAYS) / DAILY_LATE_RATE
    [ 9/20] M9   survived         billing/invoice.py:30  boundary     if rate > MAX_LATE_FEE_RATE:  ->  if rate >= MAX_LATE_FEE_RATE:
    [10/20] M10  killed           billing/invoice.py:30  negate       if rate > MAX_LATE_FEE_RATE:  ->  if rate <= MAX_LATE_FEE_RATE:
    [11/20] M11  killed           billing/invoice.py:37  boundary     if units >= 100:  ->  if units > 100:
    [12/20] M12  killed           billing/invoice.py:37  negate       if units >= 100:  ->  if units < 100:
    [13/20] M13  killed           billing/invoice.py:38  return       return Decimal("0.10")  ->  return None
    [14/20] M14  killed           billing/invoice.py:39  boundary     if units >= 20:  ->  if units > 20:
    [15/20] M15  killed           billing/invoice.py:39  negate       if units >= 20:  ->  if units < 20:
    [16/20] M16  killed           billing/invoice.py:41  return       return Decimal("0")  ->  return None
    [17/20] M17  killed           billing/invoice.py:45  boolean      tax_exempt: bool = False) -> Decimal:  ->  tax_exempt: bool = True) -> Decimal:
    [18/20] M18  killed           billing/invoice.py:48  arithmetic   gross = unit_price * units  ->  gross = unit_price / units
    [19/20] M19  killed           billing/invoice.py:49  arithmetic   net = gross - gross * volume_discount(units)  ->  net = gross - gross / volume_discount(units)
    [20/20] M20  killed           billing/invoice.py:51  arithmetic   net = net + net * VAT_RATE  ->  net = net + net / VAT_RATE
    
    Mutation score: 18/20 (90.0%)
      killed             18
      killed (timeout)    0
      survived            2
    Survivors:
      M4 billing/invoice.py:27 boundary: if days_late <= GRACE_DAYS:  ->  if days_late < GRACE_DAYS:
      M9 billing/invoice.py:30 boundary: if rate > MAX_LATE_FEE_RATE:  ->  if rate >= MAX_LATE_FEE_RATE:
    
    Restore check:
      OK    billing/invoice.py  sha256 and modification time match original
    

    ▸ Tool output: mutation check

    $ python3 ~/.claude/skills/mutation-verified-test-generator/scripts/mutate.py --target billing/invoice.py --max-mutants 29 --show-mutants -- .venv/bin/python -m pytest tests/test_invoice.py -q -p no:cacheprovider 2>&1 | grep -v -E "^\[.*killed " 
    Mutants: 29 candidates, 29 selected (cap 29).
    Baseline: running tests on the unmodified code…
    Baseline passed in 0.2s. Timeout per mutant: 30s.
    Backups: /var/folders/b1/327sn4cn0t1fb223h0pdsn140000gn/T/mvtg-backups (removed after a verified restore; if this process is killed, run again with --restore).
    [ 4/29] M4   survived         billing/invoice.py:27  boundary     if days_late <= GRACE_DAYS:  ->  if days_late < GRACE_DAYS:
    [ 9/29] M9   survived         billing/invoice.py:30  boundary     if rate > MAX_LATE_FEE_RATE:  ->  if rate >= MAX_LATE_FEE_RATE:
    
    Mutation score: 27/29 (93.1%)
      killed             27
      killed (timeout)    0
      survived            2
    Survivors:
      M4 billing/invoice.py:27 boundary: if days_late <= GRACE_DAYS:  ->  if days_late < GRACE_DAYS:
      M9 billing/invoice.py:30 boundary: if rate > MAX_LATE_FEE_RATE:  ->  if rate >= MAX_LATE_FEE_RATE:
    
    Restore check:
      OK    billing/invoice.py  sha256 and modification time match original
    

    Final checks: confirm the source is restored, then run the full suite.


    I added 51 tests to tests/test_invoice.py. To check they catch bugs, I planted 29 small bugs ("mutants") in billing/invoice.py, one at a time, and the tests caught 27. The other two can't be caught by any test, because they never change the result. The old single test caught 9 of 20.

    Source restored: billing/invoice.py is back byte-for-byte. The mutation script's checksum and timestamp check passed after every run, and git diff --exit-code on the file is clean. The only changed file is tests/test_invoice.py. The full suite passes: 52 tests.

    Tests added

    AreaCovers
    parse_amountStripping commas, $ and spaces; output always has two decimals; rounding half up ("0.005" → 0.01, "2.665" → 2.67); zero accepted; empty, blank or bare $ rejected; junk ("1.2.3", "12$") rejected; negatives rejected ("-0.01", "$-0.01")
    late_feeNo fee for days −1, 0, 1 and 5; 1% a day from day 6; capped at 25% from day 30 (days 29, 30, 31, 100 checked); fee scales with the amount; rounding (0.50 at 6 days → 0.01)
    volume_discountEach tier at its edges: 1/19 → 0, 20/21/99 → 5%, 100/500 → 10%
    invoice_totalOne unit with VAT; tax-exempt; discounts at 19, 20 and 100 units, with and without VAT; late fee with VAT, tax-exempt, and within grace; final rounding; units 0 and −1 rejected

    Mutation score

    RunTestsScoreKilledKilled (timeout)Survived
    Existing tests145.0%9011
    First draft5290.0%1802
    All 29 mutants (extra run)5293.1%2702

    By default the check only runs 20 of the 29 possible mutants. Each run took about 0.1s, so I also ran all 29. There was no strengthening round: the two that survived can't be caught by any test.

    Test command: .venv/bin/python -m pytest tests/test_invoice.py -q -p no:cacheprovider

    The two survivors

    IdLocationChangeWhy no test catches it
    M4billing/invoice.py:27days_late <= GRACE_DAYS → <On day 5 the fee becomes amount × 0, which is still 0.00. The only visible difference is -0.00 for a negative amount, which nothing in the code passes.
    M9billing/invoice.py:30rate > MAX_LATE_FEE_RATE → >=At exactly 25% it swaps 0.25 for 0.25, so the result is the same.

    Bug found (source not changed)

    • parse_amount lets some invalid input through with the wrong error type. Its docstring says it rejects junk, but "NaN", "Infinity" and "sNaN" raise decimal.InvalidOperation instead of ValueError. Code that catches ValueError around this function would miss them. They pass the Decimal() parse and fail later, in the < 0 check or the rounding step. I didn't add a test for this, so the suite stays green. One fix is to check value.is_finite() after parsing and raise ValueError if it isn't. I can make that fix, or add the test marked as an expected failure (xfail(strict=True)).

    Behaviour worth confirming

    • The late fee is charged on the total after discount and VAT, so VAT gets a late fee on it too. The tests treat this as intended (10 × 2 units, 10 days late → 25.20). If the fee should be on the pre-VAT amount, that test will need changing.

    Connects securely to your tools. The creator never sees your data.

    About this skill

    The problem

    Generated tests usually pass. That's the problem: a test that passes on correct code tells you nothing until you know it would fail on broken code. A suite can execute every line and still miss an off-by-one in a discount tier, an inverted cap, or a deleted validation call. Line coverage can't see that. Mutation testing can.

    What this skill does differently

    It doesn't just claim the tests are good: it measures it. After writing tests, the agent runs a mutation check. It makes small, realistic bugs in your source file one at a time (< → <=, + → -, && → ||, true → false, a return value blanked, a call removed), runs your tests after each, and counts how many were caught. Survivors point at exact gaps. The agent adds or tightens tests for them, re-runs the check, and explains any survivor that genuinely can't be caught.

    Tests are written before the mutation check runs. When the agent scores your existing tests, it sees only the score, not the list of planted bugs. The first-draft score therefore measures tests written from the code's behaviour, not tests tailored to known mutants.

    Who it's for

    Developers and teams who want tests they can trust, especially for logic where boundaries matter: pricing, billing, permissions, validation, parsing, date and limit handling. Also useful for checking whether an existing suite would actually catch a regression in a file before you refactor it.

    What the agent does, step by step

    1. Detects your language, test framework, test layout, naming, assertion and mocking style, and the narrowest command that runs the relevant tests.
    2. Checks there's a restore point: a clean git status for the target, or the bundled script's own backup.
    3. Reads the target and plans the tests: happy paths, boundaries, edge cases, error paths, and what will be faked and why.
    4. If tests already exist, scores them first (score only).
    5. Writes tests in your project's style and makes them pass on the unmodified code. If a test reveals a likely bug in the source, it reports it and doesn't change your source.
    6. Runs the mutation check, strengthens tests against survivors (up to two rounds), and re-runs.
    7. Verifies your source file is restored byte-for-byte, then runs your full test suite once.
    8. Reports: tests added and what each covers, the score for each run, every surviving mutant with an explanation, and how the restore was verified.

    Example output

    From a pytest project (an invoicing module with late fees, a grace period, a fee cap and discount tiers):

    | Run                 | Tests | Mutation score |
    |---------------------|-------|----------------|
    | Existing tests only |   1   | 45% (9/20)     |
    | First draft         |  52   | 90% (18/20)    |
    | All 29 mutants      |  52   | 93% (27/29)    |
    
    Survivors (both explained as equivalent, so no test can catch them):
      days_late <= GRACE_DAYS → <   (at exactly day 5 the fee is 0.00 either way)
      rate > MAX_LATE_FEE_RATE → >= (capping 25% to 25% changes nothing)
    
    Restore check:
      OK  billing/invoice.py  sha256 and modification time match original
    
    Bug found (source not changed):
      parse_amount("NaN") raises decimal.InvalidOperation instead of ValueError
    

    These are examples, not promises. In testing, scores rose from 25% to 95% (TypeScript + Vitest), 50% to 100% (JavaScript + Jest) and 45% to 90% (Python + pytest) on small sample projects. On a real open-source TypeScript CLI tested with node:test, two files with decent existing tests went from about 82% to 100% each. Your results depend on your code and your existing tests.

    How long it takes

    Plan on a few minutes per file. The skill works on one source file per run by default. Each run of the check takes roughly (your test command's run time) × (number of mutants, 20 by default), so keep the test command narrow (the skill does this for you). If you name several files, the agent finishes and reports one before starting the next.

    Safety, and what runs on your machine

    • The bundled scripts/mutate.py (Python 3.8+, standard library only) runs your project's own test command as a subprocess: once on unmodified code, then once per mutant. It runs without a shell, with a timeout.
    • It only ever writes to the target file (one mutant at a time, restored after every run, even if a run errors or is interrupted) and to its own backup folder in your system temp directory.
    • It verifies the restore by SHA-256 and modification time. If the process is killed outright, the next run refuses to start until you restore from the backup with one command.
    • It never overwrites an edit it didn't make.
    • The script makes no network calls, reads no environment variables and installs nothing. The agent asks before installing anything and uses npx --no so missing tools fail instead of downloading.
    • If Stryker or mutmut is already installed, the agent offers to use it instead, scoped to the target file.
    • No Python? The agent applies the same mutation operators by hand with its own edit tool, under the same safety rules.

    Supported

    Fully supported (tested)

    How to use

    Skills on Agensi run inside your AI agent. Copy the prompt, paste it into your agent, and it connects to Agensi and installs this skill for you. If a payment is needed, your agent shows it to you first.

    Reviews

    No reviews yet

    Be one of the first to try it. Every listed skill passes our trust checks below.

    Security scanned

    Passed our 8-point scan before listing

    Fresh listing

    Recently published to Agensi

    Money-back guarantee

    Doesn't do what its page promises? You get your money back.

    Trust & safety

    Security scanned

    Verified clean 1 day ago

    • Passed all security checks, Safe to install

    Requires

    Python 3.8+ (recommended; without it the agent applies mutations by hand)
    Git (recommended; otherwise the skill makes a backup first)
    Your project's test runner: Jest, Vitest or pytest
    vitest
    Node.js
    jest
    tsx
    mocha
    stryker
    Git

    Listed1 day ago

    What's inside

    Frequently Asked Questions