Stryker Mutation Testing: Measure Real Test Quality
Code coverage tells you which lines were run. Mutation testing tells you whether your tests actually verify the code's behaviour. Stryker introduces hundreds of tiny bugs into your codebase one at a time and checks if your tests catch them. If a test suite scores 95% coverage but only 40% mutation score, the tests are not doing their job.
What Is Mutation Testing?
Mutation testing is a technique where automated tooling makes small, deliberate changes (mutations) to your source code and checks whether your tests detect each change. If the test suite still passes after a mutation, the mutant "survived" — meaning your tests have a blind spot at that exact point in the code.
The mutation score is: (killed mutants / total mutants) × 100. A score of 75% means 25% of the artificial bugs slipped past your tests undetected.
How Stryker Works
| Mutant Status | Meaning | Action |
|---|---|---|
| Killed | A test failed — bug was caught | Good, no action needed |
| Survived | All tests passed — bug escaped | Write a test that catches this |
| No Coverage | No test even ran this code | Add coverage first |
| Timeout | Mutant caused an infinite loop | Counts as killed |
Installation and stryker.config.mjs
npm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner
# or for Vitest:
npm install --save-dev @stryker-mutator/core @stryker-mutator/vitest-runner // stryker.config.mjs
export default {
testRunner: 'jest', // or 'vitest'
coverageAnalysis: 'perTest', // fastest: only re-run relevant tests
mutate: ['src/**/*.ts', '!src/**/*.spec.ts'],
reporters: ['html', 'clear-text', 'progress'],
thresholds: { high: 80, low: 60, break: 50 },
disableTypeChecks: true, // speeds up runs significantly
}; Running Stryker
# Run mutation tests:
npx stryker run
# Run only on changed files (faster in development):
npx stryker run --mutate "src/auth/**/*.ts"
# Output: reports/mutation/mutation.html — open in browser for full report Common Surviving Mutant Types
| Mutant Type | Example Change | What to Add |
|---|---|---|
| Boundary condition | age > 18 → age >= 18 | Test with value 18 |
| Boolean flip | isActive && isValid → isActive || isValid | Test where one condition is false |
| Return value | return result → return undefined | Assert the return value explicitly |
| String literal | 'error' → '' | Assert error message content |
Mutation Score Thresholds
The thresholds config sets three values. high (default 80): scores at or above are shown in green. low (default 60): scores below are shown in red. break (default null): if set, Stryker exits with a non-zero code below this threshold, blocking CI. Set break: 50 as a minimum quality gate, and aim to push toward 70-80% over time as you improve your test suite.
Related Products
on GumroadDeveloper Productivity Bundle
Stop rebuilding the same setup every project. 51 VSCode snippets, 40 Git aliases, 5 Docker Compose stacks, 5 GitHub Actions workflows, 5 README templates, and a dotfiles kit. Set up a professional dev environment in under an hour.
Buy on Gumroad →AI Prompt Engineering Toolkit
58 structured prompt templates for Claude, ChatGPT, and Gemini — organized by use case. Code Review, Debugging, Documentation, Architecture, and 10 model-specific tips. Stop writing prompts from scratch.
Buy on Gumroad →Developer Cheat Sheet Bundle
10 print-ready cheat sheets for the tools developers use every day: Git, Docker, Linux CLI, SQL, Regex, HTTP Status codes, VS Code shortcuts, Bash scripting, jq/JSON, and Markdown.
Buy on Gumroad →You Might Also Need
JSON Formatter & Validator
Format, beautify, and validate JSON online. Paste your JSON to instantly pretty-print it with 2-space, 4-space, or tab indentation. Minify JSON too. Free, runs in your browser.
Open free tool →
Regex Tester
Free regex tester. Test regular expressions against multiple strings with expected match/no-match assertions — see instant pass/fail results. Built-in presets for email, URL, IPv4, and phone. Pro: unl
Open free tool →
Base64 Encoder & Decoder
Free Base64 Encoder & Decoder. Convert text, images, and any file to Base64 and back. Supports URL-safe Base64, image data URIs, and file download from Base64. Runs 100% in your browser — no data sent
Open free tool →
More Free Tools
Related Articles
curl 深度使用手冊:API 測試從入門到進階
curl 是每位開發者必備的 CLI 工具。本文涵蓋 GET/POST/PUT/DELETE 請求、認證、header 設定、檔案上傳、除錯技巧,以及 REST API 測試的完整實戰範例。
Best AI Code Review Tools 2026: CodeRabbit, Sourcery, DeepCode & Bito Compared
Compare top AI code review tools for 2026: CodeRabbit, Sourcery, DeepCode, and Bito. Features, pricing, GitHub/GitLab integration, and choosing the right tool for your team.
AI Code Review Tools 2026: GitHub Copilot, CodeRabbit, Qodo Merge Ranked
Compare AI code review tools for 2026: GitHub Copilot code review, CodeRabbit, Qodo Merge, Sourcery, and PR-Agent. Pricing, accuracy, and GitHub integration.
AI Coding Tools Comparison 2026: GitHub Copilot vs Cursor vs Windsurf vs Cline
Comprehensive comparison of the best AI coding tools in 2026: GitHub Copilot, Cursor, Windsurf, and Cline. Pricing, features, model support, and real-world benchmarks.
AI Pair Programming in 2026: GitHub Copilot vs Cursor vs Claude Code
Master AI pair programming in 2026. Compare GitHub Copilot, Cursor, and Claude Code for autocomplete, chat, and agentic workflows. Includes setup guides and productivity tips.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →Frequently Asked Questions
What is the difference between code coverage and mutation score?▼
Code coverage tells you which lines of code were executed during tests. It says nothing about whether your assertions actually verify the behaviour of that code. Mutation score measures how many of Stryker's artificial bugs (mutants) your tests caught and killed. A test that covers a line but has no assertions will show 100% coverage but 0% mutation score. Mutation score is a much stronger indicator of test quality.
What is a mutant in Stryker?▼
A mutant is a small, targeted change Stryker makes to your source code — one at a time — to see if your tests detect it. Examples: changing && to ||, flipping > to >=, replacing a return value with undefined, or deleting a conditional block. Stryker runs the full test suite against each mutant. A 'killed' mutant means at least one test failed, proving those tests guard that behaviour. A 'survived' mutant means no test caught the change — a gap in your suite.
How do I configure Stryker with Vitest?▼
Install the Vitest runner: npm install --save-dev @stryker-mutator/core @stryker-mutator/vitest-runner. In stryker.config.mjs, set testRunner: 'vitest' and coverageAnalysis: 'perTest'. For large projects, add disableTypeChecks: true to significantly speed up runs. Stryker will use your existing vitest.config.ts automatically.
What mutation score threshold should I aim for?▼
For business-critical code (payment logic, auth, data validation), aim for 80%+. For general application code, 60-70% is a realistic goal. Scores below 40% typically indicate tests that were written to satisfy coverage requirements rather than to verify behaviour. Do not chase 100% — some mutants are inherently equivalent (the mutation produces the same behaviour) and are not worth the test complexity.
How do I ignore specific mutants in Stryker?▼
Use the mutate config to exclude files: mutate: ['src/**/*.ts', '!src/**/*.spec.ts']. To ignore specific mutant types project-wide, use the excludedMutations option: ['StringLiteral', 'BlockStatement']. For fine-grained ignoring, add a Stryker comment in source code: // Stryker disable next-line ArithmeticOperator. Use ignoring sparingly — it should document why a mutant is intentionally untested, not cover up gaps.
How does Stryker help find untested edge cases?▼
Stryker systematically mutates boundary conditions (changing > to >= or <), boolean operators (&&, ||), null checks, and return values. Surviving mutants in boundary condition code reveal tests that pass with one value but would fail with the edge case one step away. This is exactly where off-by-one bugs and null pointer errors hide. Running Stryker on a function like validateAge(age) will quickly reveal whether your tests check the boundary (18, 17, 19) or only the happy path.