stryker-mutation
Configures StrykerJS for mutation testing of JavaScript / TypeScript / React / Vue / Svelte / Node - picks the test-runner plugin (`@stryker-mutator/jest-runner`, `mocha-runner`, `vitest-runner`, `karma-runner`), authors `stryker.conf.json` with mutate globs + thresholds, runs incremental mode for PRs (only mutate changed files), and reports the mutation score. Use when a JS/TS test suite has ≥80% line coverage and the team wants to verify the tests actually catch bugs (not just touch lines).
Install with skills.sh (any agent)
npx skills add testland/qa --skill stryker-mutationstryker-mutation
Overview
Per stryker-intro (opens in new window):
"Stryker started as a pure JavaScript mutation testing framework" and now "supports most JavaScript projects, including TypeScript, React, Angular, VueJS, Svelte, and NodeJS."
Mutation testing introduces small bugs (mutants) into the production code; if tests still pass, the test suite is too weak. A green suite with high mutation-survival rate is the bug coverage hides.
When to use
Step 1 - Install
npm install --save-dev \
@stryker-mutator/core \
@stryker-mutator/jest-runner # or mocha-runner / vitest-runner / etc.Per stryker-intro (opens in new window), supported runners include Jest, Mocha, Karma, Vitest, Jasmine, Cucumber, Tap.
Step 2 - Initialize
npx stryker initGenerates stryker.conf.json:
{
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
"packageManager": "npm",
"reporters": ["progress", "clear-text", "html"],
"testRunner": "jest",
"coverageAnalysis": "perTest",
"mutate": ["src/**/*.ts", "!src/**/*.test.ts"],
"thresholds": { "high": 80, "low": 60, "break": 50 }
}Key fields:
| Field | Use |
|---|---|
mutate | Glob patterns: which files to mutate; exclude tests + types. |
testRunner | jest / mocha / vitest / karma / etc. |
coverageAnalysis | perTest (fastest - only re-run tests that touched the mutated line). |
thresholds.break | Mutation score below this → npx stryker run exits non-zero (CI gate). |
incremental | true to only mutate files changed since last run. |
concurrency | Worker count (default = CPU count). |
Step 3 - Run
npx stryker runPer-mutant output:
[Survived] Conditional Boundary
src/cart.ts:42:5
- if (item.qty < 0) throw new Error(...);
+ if (item.qty <= 0) throw new Error(...);
Tests run: 12 (all passed - mutant survived).Survived mutant = tests passed despite the introduced bug = the tests don't actually catch this regression. Add a test for qty = 0.
Step 4 - Report + threshold
Ran 142 tests.
Mutants killed: 198
Mutants survived: 24
Mutants timed out: 3
Mutants no coverage: 12
Mutation score: 84.7%Per stryker-intro (opens in new window): thresholds via the thresholds object gate the build:
The HTML report shows per-file mutation breakdown - drill in to see which mutants survived and where to add tests.
Step 5 - Incremental mode for PR runs
Full mutation runs are slow (5-30 min on medium codebases). For PRs, incremental mode only mutates changed files:
npx stryker run --incrementalThis stores state in .stryker-tmp/ (commit to git so subsequent runs benefit). Per-PR runs typically complete in <2 min.
Step 6 - CI integration
- name: Mutation testing (changed files only)
if: github.event_name == 'pull_request'
run: npx stryker run --incremental
- name: Full mutation run
if: github.ref == 'refs/heads/main' && github.event.schedule == '0 2 * * 0'
run: npx stryker run
- uses: actions/upload-artifact@v4
if: always()
with:
name: stryker-report
path: reports/mutation/Pattern: incremental on PRs (fast feedback) + full weekly run on main (catches drift in unchanged code).
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Setting break: 100 | Impossible bar; team disables. | Start at 50-60; ratchet up over time. |
Running on every PR without --incremental | 30-min PR feedback loop; team disables. | --incremental for PRs (Step 5). |
| Mutating test files | Mutants in test code don't reflect production quality. | Exclude in mutate glob (Step 2). |
| Ignoring "no coverage" mutants | Files in mutate glob without any test coverage; mutation testing wastes effort. | Either add tests OR exclude those files from mutate. |
Using coverageAnalysis: off | Re-runs entire suite per mutant; very slow. | perTest (the default; Step 2). |
| Hard-coding break threshold without team agreement | Fails surprise PRs; team disables. | Land threshold via PR with team review. |
Limitations
References
Related skills
mull-mutation
Runs Mull, the LLVM-IR mutation testing tool, against C/C++ test binaries built with Clang: covers install (the version-matched mull-NN package), the -fpass-plugin build flags for the Mull IR frontend, mull-runner invocation, the mutator catalog, path filtering, and GitHub Actions CI. Use when a C or C++ project needs mutation-score verification with the tool already chosen. Does not select among mutation tools and does not cover other languages (stryker-mutation for JS/TS, stryker-net-mutation for .NET, pitest-mutation for the JVM, mutmut-mutation for Python).
mutant-survival-triage
Normalizes a surviving-mutant record across StrykerJS, PIT, mutmut, and Mull into one shape, classifies why it survived (missing case, weak assertion, equivalent mutant, unreachable code, flaky killer), applies per-mutator heuristics for conditional-boundary, arithmetic-operator, statement-removal, and constant mutations, and drafts the specific test that would kill it. Treats equivalence as a judgment call, because deciding whether a mutant is equivalent to the original is undecidable in general, so a residual survivor rate is expected rather than a defect. Use when a mutation run has finished and the report lists surviving mutants that nobody has yet explained or turned into concrete test cases.
mutmut-mutation
Configures mutmut for Python mutation testing - `pip install mutmut`, runs via `mutmut run`, browses results via `mutmut browse` or `mutmut results`, applies surviving mutants to disk via `mutmut apply {id}`, suppresses with `# pragma: no mutate` annotations. Configures via `setup.cfg` / `pyproject.toml` with `source_paths` + per-test selection. Use for Python codebases needing mutation-quality verification of pytest / unittest suites.
pitest-mutation
Configures PIT (PITest) for mutation testing of JVM projects (Java, Kotlin via the Kotlin plugin) - wires the `pitest-maven` or `pitest-gradle-plugin` with `mutationThreshold`, `coverageThreshold`, target classes/tests filtering, runs `mvn pitest:mutationCoverage`, parses the HTML + XML reports. Use when the JVM suite needs mutation-quality verification - the canonical Java mutation testing tool, fast (PIT analyzes "in minutes rather than days").
stryker-net-mutation
Configures Stryker.NET for mutation testing of .NET Core / .NET Framework projects - installs `dotnet-stryker` global tool, scopes mutation to specific csproj, supports xUnit / NUnit / MSTest, authors `stryker-config.json` with thresholds, runs in CI. Use when a .NET test suite needs mutation-quality verification - closes the .NET ecosystem gap left by Stryker.NET being newer than the JS variant.