Mutation Testing: Are Your Tests Actually Catching Bugs? (Stryker Guide)
Your tests pass. Code coverage is 90%. But do your tests actually catch bugs? Mutation testing answers this by injecting bugs into your code and checking if tests detect them.
🎠Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.
Contents
How Mutation Testing Works
Original code: if (price > 100) { applyDiscount(); }
Mutant 1: if (price >= 100) { applyDiscount(); } // Changed > to >=
Mutant 2: if (price < 100) { applyDiscount(); } // Changed > to <
Mutant 3: if (price > 100) { /* removed */ } // Removed function call
If your tests PASS with the mutant = SURVIVED = your tests are weak
If your tests FAIL with the mutant = KILLED = your tests caught the bug
Mutation Score
Mutation Score = Killed Mutants / Total Mutants
- 90%+ = Excellent — tests catch most injected bugs
- 70-90% = Good — some gaps to address
- Below 70% = Weak — tests give false confidence
Setting Up Stryker (JavaScript/TypeScript)
npm install -D @stryker-mutator/core @stryker-mutator/typescript-checker
npx stryker init
// stryker.config.json
{
"mutate": ["src/**/*.ts", "!src/**/*.spec.ts"],
"testRunner": "jest",
"checkers": ["typescript"],
"reporters": ["html", "clear-text"],
"thresholds": {
"high": 80,
"low": 60,
"break": 50
}
}
🚀 Level Up Your Playwright
From locators to CI pipelines — build a production-grade Playwright + TypeScript framework step by step.
Common Mutation Types
| Mutation | Example | What It Tests |
|---|---|---|
| Conditional boundary | > becomes >= | Boundary value assertions |
| Negate conditional | == becomes != | Logic correctness |
| Remove call | Function call deleted | Side effect verification |
| Return value | return true becomes false | Return value assertions |
| Arithmetic | + becomes – | Calculation assertions |
| String literal | “hello” becomes “” | String content checks |
Interpreting Results
# Run mutation testing
npx stryker run
# Output:
# Mutant #1 (ConditionalBoundary): KILLED by test "should reject order over limit"
# Mutant #2 (RemoveCall): SURVIVED - no test detected removal of sendEmail()
# Mutant #3 (BooleanLiteral): KILLED by test "should return false for invalid input"
#
# Mutation score: 78% (36/46 mutants killed)
Mutation Testing vs Code Coverage
| Metric | What It Measures | Limitation |
|---|---|---|
| Code coverage | Lines executed by tests | Executed != verified. No assertions = 100% coverage, 0% confidence |
| Mutation score | Bugs actually caught by tests | Slow to run. Use on critical modules, not entire codebase |
🎓 Master Playwright End to End
Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.
