Mutation testing
Skill yigityildiz0/universal-ai-skill-library/skills/common/mutation-testing
531 searchable AI Agent Skills for Claude Code, OpenAI Codex, and OpenCode — EN/TR catalog, platform and risk notes, direct ZIPs, and curated bundles.
npx -y skills add yigityildiz0/universal-ai-skill-library --skill mutation-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 18 days oldThe repository was created 18 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Validate test quality through mutation testing to detect weak tests and reward hacking patterns. Use when verifying test suite effectiveness, improving test.
SKILL.md
11.7 KB, as published. Nobody here has run it
Mutation Testing
Validate test suite quality by introducing code mutations and verifying tests detect them. This skill implements Phase 8 of the 8-phase testing methodology - the final validation phase.
When to Use This Skill
Use this skill when you need to:
- Validate test suite effectiveness
- Detect weak or superficial tests
- Find "reward hacking" patterns
- Improve test quality beyond coverage
- Verify tests catch real bugs
- Identify tests that always pass
- Strengthen assertions
Trigger phrases: "mutation testing", "test quality", "reward hacking", "mutants", "test effectiveness", "weak tests", "mutation score", "pitest", "mutmut"
What This Skill Does
What is Mutation Testing?
Mutation testing validates test quality by:
- Making small changes (mutations) to source code
- Running tests against mutated code
- Checking if tests detect (kill) the mutations
- Measuring mutation score (% killed)
Mutation Types
| Mutation | Original | Mutated |
|---|---|---|
| Arithmetic | a + b | a - b |
| Relational | a < b | a <= b |
| Boolean | a && b | a || b |
| Return Value | return x | return null |
| Void Method | call() | removed |
| Constant | MAX = 100 | MAX = 101 |
Language-Specific Examples
Python (mutmut / pytest-mutagen)
# Install mutmut
pip install mutmut
# Run mutation testing
mutmut run --paths-to-mutate=src/
# View results
mutmut results
# Show surviving mutants
mutmut show <mutant_id>
# Generate HTML report
mutmut html
# Example: Detecting weak tests
# Original code
def calculate_discount(price: float, rate: float) -> float:
"""Calculate discounted price."""
if price < 0:
raise ValueError("Price cannot be negative")
if rate < 0 or rate > 1:
raise ValueError("Rate must be between 0 and 1")
return price * (1 - rate)
# WEAK TEST - Would pass with mutations
def test_discount_weak():
"""This test is too weak - mutants survive."""
result = calculate_discount(100, 0.1)
assert result is not None # Too weak!
assert result > 0 # Doesn't verify exact value
# STRONG TEST - Kills mutations
def test_discount_strong():
"""This test catches mutations."""
assert calculate_discount(100, 0.1) == 90.0 # Exact value
assert calculate_discount(100, 0.5) == 50.0 # Another case
assert calculate_discount(0, 0.5) == 0.0 # Edge case
def test_discount_validates_price():
"""Catches removed validation mutations."""
with pytest.raises(ValueError, match="cannot be negative"):
calculate_discount(-10, 0.1)
def test_discount_validates_rate():
"""Catches boundary mutations."""
with pytest.raises(ValueError, match="between 0 and 1"):
calculate_discount(100, -0.1)
with pytest.raises(ValueError, match="between 0 and 1"):
calculate_discount(100, 1.5)
# mutmut.conf
def pre_mutation(context):
"""Skip certain mutations."""
if context.current_source_line.strip().startswith('#'):
context.skip = True
# Run mutation testing for specific module
# mutmut run --paths-to-mutate=src/pricing.py
JavaScript/TypeScript (Stryker)
// stryker.conf.js
module.exports = {
mutator: 'typescript',
packageManager: 'npm',
reporters: ['html', 'clear-text', 'progress', 'dashboard'],
testRunner: 'jest',
coverageAnalysis: 'perTest',
jest: {
projectType: 'custom',
configFile: 'jest.config.js',
enableFindRelatedTests: true,
},
thresholds: {
high: 80,
low: 60,
break: 50 // Fail if mutation score below 50%
},
mutate: [
'src/**/*.ts',
'!src/**/*.test.ts',
'!src/**/*.d.ts'
],
timeoutMS: 60000,
concurrency: 4
};
// Run Stryker
// npx stryker run
// Example: Weak vs Strong Tests
// Source code
function calculateDiscount(price: number, rate: number): number {
if (price < 0) throw new Error('Price cannot be negative');
if (rate < 0 || rate > 1) throw new Error('Rate must be between 0 and 1');
return price * (1 - rate);
}
// WEAK TEST - Mutants survive
describe('calculateDiscount - weak', () => {
it('returns a number', () => {
const result = calculateDiscount(100, 0.1);
expect(typeof result).toBe('number'); // Doesn't verify value
});
it('returns positive value', () => {
expect(calculateDiscount(100, 0.1)).toBeGreaterThan(0); // Too vague
});
});
// STRONG TEST - Kills mutants
describe('calculateDiscount - strong', () => {
it('calculates 10% discount correctly', () => {
expect(calculateDiscount(100, 0.1)).toBe(90); // Exact value
});
it('calculates 50% discount correctly', () => {
expect(calculateDiscount(200, 0.5)).toBe(100); // Another exact case
});
it('handles zero price', () => {
expect(calculateDiscount(0, 0.5)).toBe(0); // Edge case
});
it('handles zero discount', () => {
expect(calculateDiscount(100, 0)).toBe(100); // Boundary
});
it('handles full discount', () => {
expect(calculateDiscount(100, 1)).toBe(0); // Boundary
});
it('rejects negative price', () => {
expect(() => calculateDiscount(-10, 0.1))
.toThrow('Price cannot be negative');
});
it('rejects negative rate', () => {
expect(() => calculateDiscount(100, -0.1))
.toThrow('Rate must be between 0 and 1');
});
it('rejects rate above 1', () => {
expect(() => calculateDiscount(100, 1.5))
.toThrow('Rate must be between 0 and 1');
});
});
Java (PIT / PITest)
<!-- pom.xml PIT configuration -->
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.15.0</version>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
<configuration>
<targetClasses>
<param>com.example.service.*</param>
</targetClasses>
<targetTests>
<param>com.example.service.*Test</param>
</targetTests>
<mutators>
<mutator>DEFAULTS</mutator>
<mutator>STRONGER</mutator>
</mutators>
<mutationThreshold>80</mutationThreshold>
<coverageThreshold>80</coverageThreshold>
<outputFormats>
<outputFormat>HTML</outputFormat>
<outputFormat>XML</outputFormat>
</outputFormats>
<timestampedReports>false</timestampedReports>
</configuration>
</plugin>
<!-- Run PIT: mvn test-compile pitest:mutationCoverage -->
// Example: Weak vs Strong Tests
public class PriceCalculator {
public double calculateDiscount(double price, double rate) {
if (price < 0) {
throw new IllegalArgumentException("Price cannot be negative");
}
if (rate < 0 || rate > 1) {
throw new IllegalArgumentException("Rate must be between 0 and 1");
}
return price * (1 - rate);
}
}
// WEAK TEST - Many mutants survive
class PriceCalculatorWeakTest {
@Test
void discount_returns_value() {
PriceCalculator calc = new PriceCalculator();
double result = calc.calculateDiscount(100, 0.1);
assertNotNull(result); // Too weak
assertTrue(result > 0); // Doesn't verify exact value
}
}
// STRONG TEST - Kills mutants
class PriceCalculatorStrongTest {
private PriceCalculator calculator;
@BeforeEach
void setUp() {
calculator = new PriceCalculator();
}
@Test
void calculateDiscount_with10Percent_returns90() {
assertEquals(90.0, calculator.calculateDiscount(100, 0.1), 0.001);
}
@Test
void calculateDiscount_with50Percent_returnsHalfPrice() {
assertEquals(100.0, calculator.calculateDiscount(200, 0.5), 0.001);
}
@Test
void calculateDiscount_withZeroPrice_returnsZero() {
assertEquals(0.0, calculator.calculateDiscount(0, 0.5), 0.001);
}
@Test
void calculateDiscount_withZeroRate_returnsOriginalPrice() {
assertEquals(100.0, calculator.calculateDiscount(100, 0), 0.001);
}
@Test
void calculateDiscount_withFullDiscount_returnsZero() {
assertEquals(0.0, calculator.calculateDiscount(100, 1), 0.001);
}
@ParameterizedTest
@CsvSource({
"100, 0.1, 90.0",
"200, 0.25, 150.0",
"50, 0.5, 25.0"
})
void calculateDiscount_variousInputs(double price, double rate, double expected) {
assertEquals(expected, calculator.calculateDiscount(price, rate), 0.001);
}
@Test
void calculateDiscount_withNegativePrice_throwsException() {
assertThrows(IllegalArgumentException.class,
() -> calculator.calculateDiscount(-10, 0.1));
}
@Test
void calculateDiscount_withNegativeRate_throwsException() {
assertThrows(IllegalArgumentException.class,
() -> calculator.calculateDiscount(100, -0.1));
}
@Test
void calculateDiscount_withRateAboveOne_throwsException() {
assertThrows(IllegalArgumentException.class,
() -> calculator.calculateDiscount(100, 1.5));
}
}
Prerequisites
- High code coverage achieved (Phase 7)
- Unit tests comprehensive
- Understanding of mutation testing concepts
Instructions
Step 1: Install Mutation Testing Tool
# Python
pip install mutmut
# JavaScript/TypeScript
npm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner
# Java - add PIT plugin to pom.xml
# Go
go install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest
Step 2: Configure and Run
-
Configure Target Code
- Specify source directories
- Exclude generated code
- Set timeout values
-
Run Mutation Tests
# Python mutmut run # JavaScript npx stryker run # Java mvn pitest:mutationCoverage
Step 3: Analyze Results
-
Review Surviving Mutants
- Identify weak assertions
- Find missing edge cases
- Note untested branches
-
Strengthen Tests
- Add specific assertions
- Test boundary conditions
- Cover error paths
Step 4: Set Thresholds
- Configure Minimums
- Target: 80%+ mutation score
- Warning: Below 60%
- Fail: Below 50%
Quality Checklist
- Mutation tool configured
- Initial mutation score measured
- Surviving mutants analyzed
- Weak tests strengthened
- Mutation score at 80%+
- CI/CD threshold configured
Common Weak Test Patterns
1. Vague Assertions
# BAD
assert result is not None
# GOOD
assert result == expected_value
2. Missing Boundary Tests
# BAD - Only tests middle values
assert func(50) == expected
# GOOD - Tests boundaries
assert func(0) == 0
assert func(100) == max_value
3. Missing Error Tests
# BAD - Only happy path
assert func(valid_input) == result
# GOOD - Tests error paths
with pytest.raises(ValueError):
func(invalid_input)
Related Skills
code-coverage- Coverage analysis (Phase 7)unit-tests- Unit testing (Phase 2)test-cases- Integration tests (Phase 3)
Version: 1.0.0 Last Updated: December 2025 Based on: AI Templates tests_generation/reward_hacking/
Iterative Refinement Strategy
This skill is optimized for an iterative approach:
- Execute: Perform the core steps defined above.
- Review: Critically analyze the output (coverage, quality, completeness).
- Refine: If targets aren't met, repeat the specific implementation steps with improved context.
- Loop: Continue until the definition of done is satisfied.