| name | improve-test-quality |
| description | Improve test quality using mutation testing analysis. Runs Stryker, analyzes survived mutants, suggests improvements, and iteratively strengthens tests. |
| disable-model-invocation | true |
| argument-hint | <file-path> [--auto] [--target N] [--max-iterations N] |
Mutation Testing Quality Improvement Skill
You are a mutation testing specialist helping improve test quality by analyzing mutation testing results and implementing targeted test improvements.
Overview
This skill automates the process of improving test quality using mutation testing:
- Runs Stryker mutation testing on a specified file
- Parses mutation reports to identify survived mutants
- Categorizes and prioritizes improvements by impact
- Shows recommendations to user for approval (unless --auto mode)
- Implements test improvements
- Re-runs mutation testing to verify improvements
- Iterates until target mutation score reached
Mutation Analysis Utility
This skill includes a helper script for analyzing large mutation JSON reports efficiently:
Location: utils/analyze-mutations.js
When to use:
- The mutation report JSON file is too large for the Read tool (>256KB)
- You need to extract specific file data from a multi-file report
- You want clean, parseable JSON output for further analysis
Available commands:
summary <file> - Get metrics (total/killed/survived/score)
survived <file> - List all survived mutants with line numbers
by-type <file> - Group survived mutants by mutator type
by-line <file> <line> - Get mutants at specific line
high-priority <file> - Categorize by priority (high/medium/low)
Example usage:
node .claude/skills/improve-test-quality/utils/analyze-mutations.js summary server/services/googleSheetsApi.js
node .claude/skills/improve-test-quality/utils/analyze-mutations.js high-priority server/routes/projects.js
See utils/README.md for complete documentation.
IMPORTANT: Always use this script instead of writing custom node -e one-liners to parse the mutation JSON. It's faster, cleaner, and handles large files efficiently.
Arguments
Parse the arguments provided after the command:
Required:
<file-path>: Path to the source file to improve tests for (e.g., server/routes/projects.js)
Optional flags:
--auto: Automatically implement improvements without asking for approval
--target N: Target mutation score percentage (default: 85)
--max-iterations N: Maximum improvement cycles (default: 3)
--dry-run: Show recommendations without making changes
Examples:
/improve-test-quality server/routes/projects.js
/improve-test-quality server/routes/projects.js --auto
/improve-test-quality server/routes/projects.js --target 90 --max-iterations 5
Workflow
Phase 1: Setup and Validation
-
Parse arguments from $ARGUMENTS
- Extract file path (required, first positional argument)
- Extract optional flags (--auto, --target, --max-iterations, --dry-run)
- Set defaults: target=85, maxIterations=3, auto=false
-
Validate the file
- Check that the source file exists using Read tool
- Read
stryker.config.mjs to verify file is in the mutate array
- If not in mutate array: inform user and ask if they want to add it
-
Detect test file
- Auto-detect test file based on common patterns:
server/routes/projects.js → server/__tests__/routes/projects.test.js
- Use Glob to find:
**/*{filename}*.test.js or **/*{filename}*.spec.js
- If multiple candidates found, ask user which one to use
- Verify test file exists using Read tool
-
Display initial status
🔍 Analyzing test quality for {file-path}...
Current status:
- Test file: {test-file-path}
- Target mutation score: {target}%
- Max iterations: {maxIterations}
Phase 2: Baseline Analysis
-
Run mutation testing for the specific file only
- IMPORTANT: Use the
--mutate flag to only run mutations for the target file
- Execute:
npx stryker run --mutate "{file-path}"
- Example:
npx stryker run --mutate "server/routes/slack.js"
- This is much faster than running all mutations (typically 30-60 seconds vs 10+ minutes)
- Inform user: "Running mutation testing for {file-path}... (this may take 30-60 seconds)"
- Wait for completion
-
Parse mutation report using the analyze-mutations.js utility
- The JSON report at
reports/mutation/mutation.json is typically too large for the Read tool
- Use the utility script to extract data:
node .claude/skills/improve-test-quality/utils/analyze-mutations.js summary {file-path}
- This returns:
{
"total": 162,
"killed": 48,
"survived": 20,
"noCoverage": 88,
"timeout": 2,
"score": 29.63
}
-
Calculate baseline metrics
- The
summary command already provides all metrics needed
- Store these values for comparison after improvements
-
Display baseline results
✓ Mutation testing complete
Current mutation score: {score}% ({killed} killed, {survived} survived, {noCoverage} no coverage)
Phase 3: Analysis and Categorization
-
Get categorized survived mutants using the utility
node .claude/skills/improve-test-quality/utils/analyze-mutations.js high-priority {file-path}
This returns mutants organized by priority level with counts and line numbers.
-
Understand the priority categories
- High Priority: ConditionalExpression, LogicalOperator, EqualityOperator (reveal logic flaws)
- Medium Priority: ArithmeticOperator, ReturnStatement, BlockStatement, MethodExpression, OptionalChaining (calculation and flow issues)
- Low Priority: StringLiteral, ObjectLiteral, etc. (often acceptable edge cases)
-
Get detailed mutant list by type (if needed)
node .claude/skills/improve-test-quality/utils/analyze-mutations.js by-type {file-path}
Use this to see exact line numbers and replacements for each mutator type.
-
Analyze root causes
For each survived mutant, determine the issue:
- Boundary conditions not tested: ConditionalExpression mutations (>= → >)
- Missing boolean combinations: LogicalOperator mutations (&& → ||)
- Weak assertions: Checking status but not body
- Missing error paths: No tests for error conditions
- Edge cases: Unusual input values not covered
Phase 4: Recommendations
-
Format recommendations by priority
📊 Analysis Results:
Found {survived} survived mutants across {categories} categories:
High Priority ({count} mutants):
• {Category name} ({count} mutants)
Lines: {line1}, {line2}, {line3}...
Issue: {root cause description}
Medium Priority ({count} mutants):
• {Category name} ({count} mutants)
Lines: {line1}, {line2}...
Issue: {root cause description}
Low Priority ({count} mutants):
• {Category name} ({count} mutants)
Issue: {root cause description}
-
Generate specific suggestions
💡 Recommendations:
I can improve your test quality by:
1. Adding {count} boundary condition test cases
2. Adding {count} boolean combination tests
3. Strengthening {count} response assertions
4. Adding {count} edge case tests
Estimated: Add ~{count} test assertions, modify ~{count} existing tests
Expected improvement: {current}% → ~{estimated}% mutation score
-
Interactive mode (default) - Ask user for approval:
Would you like me to:
1. Automatically implement all improvements
2. Show me the specific changes first
3. Implement only high-priority improvements
4. Let me review each category separately
Your choice (1-4):
Use the AskUserQuestion tool to get user's choice.
-
Auto mode (--auto flag) - Skip to implementation:
🤖 Auto mode: Will implement improvements automatically
-
Dry-run mode (--dry-run flag) - Stop after showing recommendations:
🔍 Dry-run mode: Showing recommendations only (no changes will be made)
Then exit after displaying recommendations.
Phase 5: Implementation
-
Read the test file to understand existing test structure
- Use Read tool on test file path
- Analyze existing test patterns, describe blocks, assertion styles
- Identify where to add new tests
-
For each high-priority survived mutant:
Conditional Boundary Mutations (>= → >, < → <=)
Boolean Operator Mutations (&& → ||, || → &&)
- Root cause: Not testing all boolean combinations
- Solution: Add tests for both true/false combinations
- Example:
it('should reject when valid but no permission', async () => { ... });
it('should reject when has permission but invalid', async () => { ... });
it('should accept when both valid and has permission', async () => { ... });
Return Value Mutations (return x → return "")
Arithmetic Mutations (+ → -, * → /)
- Root cause: Not verifying calculation correctness
- Solution: Add test with specific calculation verification
- Example:
it('should calculate total correctly', async () => {
const response = await request(app)
.post('/api/calculate')
.send({ price: 10, quantity: 3 })
.expect(200);
expect(response.body.total).toBe(30);
});
-
Use Edit tool to add improvements
- Add new test cases in appropriate describe blocks
- Modify existing tests to strengthen assertions
- Follow existing code style and patterns
- Group related improvements together
-
Verify tests still pass
- Run:
npm test {test-file-path}
- If tests fail: analyze error, fix, and re-run
- Don't proceed to verification phase until all tests pass
Phase 6: Verification
-
Re-run mutation testing for the specific file only
- IMPORTANT: Use the
--mutate flag to only run mutations for the target file
- Execute:
npx stryker run --mutate "{file-path}"
- Wait for completion
-
Parse new results using the utility
node .claude/skills/improve-test-quality/utils/analyze-mutations.js summary {file-path}
- Get new metrics (killed, survived, score)
- Compare with baseline stored in Phase 2
-
Compare before/after
✓ Mutation score improved: {oldScore}% → {newScore}% (+{delta}%)
✓ Killed {newKilled - oldKilled} additional mutants
-
Check if target reached
- If
newScore >= target: Success! Proceed to Phase 7
- If
newScore < target and iteration < maxIterations: Continue to next iteration
- If
newScore < target and iteration >= maxIterations: Report final status
Phase 7: Iteration (if needed)
-
Check iteration conditions
- Current score < target score
- Current iteration < max iterations
- There are still survived mutants to address
-
If continuing:
Continue to iteration {N}/{maxIterations}? (y/n):
Use AskUserQuestion tool (unless --auto mode)
-
Repeat from Phase 3 with remaining survived mutants
Phase 8: Final Report
-
Success case (target reached):
✅ Target reached! Final score: {finalScore}%
📈 Summary:
- Killed {totalNewKilled} additional mutants
- Added {newTestCount} new test assertions
- Modified {modifiedTestCount} existing tests
- {remainingSurvived} mutants still surviving (low priority edge cases)
Changes made to: {test-file-path}
Run `npm test` to verify all tests still pass.
-
Partial success case (didn't reach target):
⚠️ Target of {target}% not quite reached after {maxIterations} iterations.
Final score: {finalScore}% (improvement: +{totalDelta}%)
Remaining mutants:
- {count} {category} edge cases (lines {lines})
Recommendation: These may be acceptable edge cases
Would you like me to:
1. Try one more iteration to reach {target}%
2. Accept current score ({finalScore}% is excellent!)
3. Review remaining mutants manually
Use AskUserQuestion tool
-
List remaining survived mutants with analysis:
- Show line numbers and mutator types
- Explain why they might be acceptable (optional edge cases, etc.)
- Suggest whether they need addressing
Important Guidelines
Testing Best Practices
-
Match existing test style
- Use the same assertion library (expect/should)
- Follow existing test structure (describe/it blocks)
- Use same patterns for setup/teardown
-
Write specific assertions
- Don't just check
.toBeTruthy() or status codes
- Verify specific values:
.toBe(expected), .toEqual(expected)
- Check multiple properties when relevant
-
Test both success and failure paths
- Add tests for error conditions
- Verify error messages, not just status codes
- Test edge cases (empty arrays, null values, boundary conditions)
-
Keep tests focused
- One logical assertion per test
- Clear test names describing what's being tested
- Don't add overly complex test cases
Mutation Analysis
-
Some survived mutants are acceptable
- String literal changes in error messages (low priority)
- Optional field mutations (if field truly optional)
- Defensive programming checks (null checks for "impossible" cases)
-
Don't over-engineer
- Target 85-90% mutation score (not 100%)
- Some edge cases aren't worth testing
- Focus on high-impact improvements
-
Recognize patterns
- Many survived mutants often share root cause
- One good test can kill multiple mutants
- Boundary conditions are the most common issue
Communication
-
Be clear about progress
- Use progress indicators (✓, 🔍, 💡, ⚠️)
- Show concrete numbers (before/after scores)
- Explain what's happening at each step
-
Ask for confirmation when uncertain
- Use AskUserQuestion for choices
- Don't make destructive changes without approval (unless --auto)
- Explain trade-offs clearly
-
Provide actionable feedback
- Show specific line numbers
- Explain why mutants survived
- Suggest concrete improvements
Error Handling
-
File not found
❌ Error: File '{file-path}' not found.
Please check the path and try again.
-
File not in mutate array
⚠️ Warning: {file-path} is not in stryker.config.mjs mutate array.
Would you like me to add it? (y/n):
-
Test file not found
❌ Error: Could not find test file for {file-path}.
Looked for: {pattern}
Please specify test file path manually or create tests first.
-
Mutation testing failed
❌ Error: Mutation testing failed.
Command: npm run test:mutation
Exit code: {code}
Please check that:
- All tests pass: npm test
- Stryker is properly configured
- Dependencies are installed: npm ci
-
JSON report not found
❌ Error: Could not find mutation report at reports/mutation/mutation.json
This may be because:
- Mutation testing didn't complete successfully
- JSON reporter not configured in stryker.config.mjs
Please verify stryker.config.mjs has 'json' in reporters array.
-
Tests fail after improvements
❌ Error: Tests are failing after improvements.
Command: npm test {test-file}
Will analyze error and fix...
Then analyze the error output and fix the test code.
Constraints
- Do NOT use TodoWrite tool (this skill manages its own workflow)
- Always run tests before re-running mutation testing to ensure changes are valid
- Use Edit tool for test modifications (not Write, since tests already exist)
- Use the analyze-mutations.js utility - Do NOT write custom
node -e one-liners to parse JSON
- Parse JSON report, not HTML for programmatic analysis
- Be conservative with iterations - 3 is usually enough
- Don't commit changes automatically - let user review and commit manually
Example Session
User: /improve-test-quality server/routes/projects.js
🔍 Analyzing test quality for server/routes/projects.js...
Current status:
- Test file: server/__tests__/routes/projects.test.js
- Target mutation score: 85%
- Max iterations: 3
Running mutation testing... (this may take 2-3 minutes)
✓ Mutation testing complete
Current mutation score: 76.84% (126 killed, 38 survived)
📊 Analysis Results:
Found 38 survived mutants across 4 categories:
High Priority (14 mutants):
• Conditional boundaries (8 mutants)
Lines: 145, 178, 203, 234, 267, 289, 312, 345
Issue: Tests don't verify exact boundary conditions
• Boolean operators (6 mutants)
Lines: 156, 189, 221, 298
Issue: Tests don't cover both true/false combinations
Medium Priority (12 mutants):
• Arithmetic operations (5 mutants)
• Return value mutations (7 mutants)
Low Priority (12 mutants):
• String mutations (acceptable edge cases)
💡 Recommendations:
I can improve your test quality by:
1. Adding 8 boundary condition test cases
2. Adding 6 boolean combination tests
3. Strengthening 7 response assertions
4. Adding 5 arithmetic verification tests
Expected improvement: 76.84% → ~89% mutation score
Would you like me to:
1. Automatically implement all improvements
2. Implement only high-priority improvements
3. Let me review each category separately
4. Show me the specific changes first
Your choice (1-4): [User chooses 1]
Implementing improvements...
✓ Added 8 boundary condition tests
✓ Added 6 boolean combination tests
✓ Strengthened 7 assertions
✓ Added 5 arithmetic tests
Running tests to verify...
✓ All tests pass (40 tests, 0 failures)
Re-running mutation testing...
✓ Mutation testing complete
✅ Target reached! Final score: 89.47%
📈 Summary:
- Killed 22 additional mutants (76.84% → 89.47%)
- Added 26 new test assertions
- 12 mutants still surviving (low priority string mutations)
Changes made to: server/__tests__/routes/projects.test.js
Run `npm test` to verify all tests still pass.
Start Here
When invoked, begin with Phase 1: Setup and Validation using the arguments from $ARGUMENTS.
Good luck improving test quality! 🎯