From devflow-enforcer
Run mutation testing with Stryker to measure test quality. Identifies weak tests that don't catch code mutations.
npx claudepluginhub xarlord/devflow-enforcer --plugin devflow-enforcerThis skill uses the workspace's default tool permissions.
This skill runs mutation testing to measure test suite quality:
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
This skill runs mutation testing to measure test suite quality:
Verify that tests actually catch bugs. Use this skill:
1. ANALYZE codebase structure
2. CONFIGURE mutation testing
3. GENERATE mutations
4. RUN tests against each mutation
5. CALCULATE mutation score
6. REPORT surviving mutants
7. SUGGEST test improvements
| Parameter | Type | Description | Required |
|---|---|---|---|
| files | string[] | Files to mutate | No |
| threshold | number | Minimum mutation score | No (default: 80) |
| reporters | string[] | Report formats | No |
| concurrency | number | Parallel processes | No |
// stryker.config.js
module.exports = {
packageManager: 'npm',
reporters: ['html', 'clear-text', 'progress', 'dashboard'],
testRunner: 'jest',
coverageAnalysis: 'perTest',
mutate: ['src/**/*.ts', '!src/**/*.spec.ts'],
thresholds: {
high: 80,
low: 60,
break: 70,
},
jest: {
projectType: 'custom',
config: require('./jest.config.js'),
},
};
# pyproject.toml
[tool.mutmut]
paths_to_mutate = ["src/"]
runner = "pytest -x"
tests_dir = ["tests/"]
<!-- pom.xml -->
<plugin>
<groupId>io.stryker-mutator</groupId>
<artifactId>stryker4s-maven-plugin</artifactId>
<configuration>
<mutate>
<element>src/main/java/**/*.java</element>
</mutate>
<thresholds>
<high>80</high>
<low>60</low>
<break>70</break>
</thresholds>
</configuration>
</plugin>
| Operator | Description | Example |
|---|---|---|
| Arithmetic | Change operators | + → - |
| Conditional | Change conditions | > → >= |
| Equality | Change operators | == → != |
| Boolean | Invert values | true → false |
| String | Change strings | "hello" → "" |
| Array | Change lengths | .length → .length - 1 |
| Object | Remove calls | obj.method() → "" |
# Mutation Testing Report
**Date:** [timestamp]
**Framework:** Stryker
**Duration:** 12m 34s
## Mutation Score
| Metric | Value | Target |
|--------|-------|--------|
| **Mutation Score** | **85%** | 80% ✅ |
| Killed | 170 | - |
| Survived | 20 | - |
| Timeout | 5 | - |
| No Coverage | 5 | - |
| **Total Mutants** | **200** | - |
## File Scores
| File | Score | Killed | Survived |
|------|-------|--------|----------|
| auth.service.ts | 95% | 57 | 3 |
| user.service.ts | 82% | 45 | 10 |
| payment.service.ts | 78% | 39 | 11 |
| email.service.ts | 88% | 35 | 5 |
## Surviving Mutants (Need Tests)
### 1. auth.service.ts:45
**Original:**
```typescript
if (user.isActive) {
return generateToken(user);
}
Mutated:
if (false) { // Changed: user.isActive → false
return generateToken(user);
}
Test Needed: Verify that inactive users cannot get tokens
Suggested Test:
it('should reject inactive users', async () => {
user.isActive = false;
await expect(authService.login(user))
.rejects.toThrow('User is inactive');
});
Original:
const fee = amount * 0.03;
Mutated:
const fee = amount * 0; // Changed: 0.03 → 0
Test Needed: Verify fee calculation
Suggested Test:
it('should calculate correct fee', () => {
const fee = paymentService.calculateFee(100);
expect(fee).toBe(3); // 3% of 100
});
| Reason | Count |
|---|---|
| Equivalent mutation | 5 |
| Not worth testing | 3 |
payment.service.ts fee calculationuser.service.ts
## Example Usage
### Example 1: Run Full Mutation Testing
USER: /mutation-test
SKILL OUTPUT:
Duration: 12m 34s
| Category | Count |
|---|---|
| Killed | 170 |
| Survived | 20 |
| Timeout | 5 |
| No Coverage | 5 |
| Total | 200 |
View Report: reports/mutation/html/index.html
### Example 2: Test Specific File
USER: /mutation-test auth.service.ts
SKILL INPUT: { "files": ["src/services/auth.service.ts"] }
SKILL OUTPUT:
File: auth.service.ts Duration: 2m 15s
| Mutant Type | Killed | Survived |
|---|---|---|
| Conditional | 25 | 2 |
| Equality | 18 | 1 |
| Boolean | 14 | 0 |
| Total | 57 | 3 |
// Original
if (user.isActive) {
// Mutated
if (false) {
Add Test:
it('should reject inactive users', async () => {
const inactiveUser = { ...user, isActive: false };
await expect(service.login(inactiveUser))
.rejects.toThrow('User is inactive');
});
### Example 3: Set Threshold
USER: /mutation-test --threshold 90
SKILL INPUT: { "threshold": 90 }
SKILL OUTPUT:
Threshold: 90% Actual Score: 85%
❌ Build Failed: Mutation score below threshold
Priority fixes needed:
Run npx stryker run to see full report.
## Running Mutation Tests
### JavaScript/TypeScript
```bash
npx stryker run
mutmut run
mvn stryker4s:run
dotnet stryker
# .github/workflows/mutation.yml
name: Mutation Testing
on: [push, pull_request]
jobs:
mutation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npx stryker run
env:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_TOKEN }}
This skill integrates with:
coverage-analysis: Combined quality metricstest-generation: Generate missing tests