关于 mutation-testing
面向使用变异测试验证测试能否发现缺陷的开发者,提供 TypeScript/JavaScript 的 Stryker 和 Python 的 mutmut 指导,以及结果解读和强化薄弱测试的方法。
主要功能
- 01变异测试概念与评分
- 02TypeScript 和 JavaScript 的 Stryker 配置
- 03Bun 原生运行器指南
- 04Python mutmut 命令
- 05改进薄弱测试的模式
适用场景
- 01检查测试能否捕获代码变更
- 02查找测试套件中的薄弱断言
- 03增量运行变异测试
- 04改善边界条件覆盖
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill mutation-testing安装后在 Claude Code 中输入/mutation-testing确认已加载。
详细介绍
变异测试
变异测试的专业知识——通过引入刻意的代码变异,验证测试是否确实能捕获 Bug。
核心概念
- 变异体:自动引入的小幅代码改动
- 被杀死:引入变异后测试失败(好——测试捕获了 Bug)
- 存活:引入变异后测试通过(不好——测试较弱)
- 得分:被杀死的变异体所占百分比(目标为 80% 以上)
TypeScript/JavaScript(Stryker)
Vitest 运行器
安装
# Using Bun
bun add -d @stryker-mutator/core @stryker-mutator/vitest-runner
# Using npm
npm install -D @stryker-mutator/core @stryker-mutator/vitest-runner
配置
// stryker.config.mjs
export default {
packageManager: 'bun',
reporters: ['html', 'clear-text', 'progress'],
testRunner: 'vitest',
coverageAnalysis: 'perTest',
mutate: ['src/**/*.ts', '!src/**/*.test.ts'],
thresholds: { high: 80, low: 60, break: 60 },
incremental: true,
}
Bun 原生运行器(bun test)
适用于直接使用 bun test 的项目(而非 Vitest)。
要求
- Bun >= 1.3.7(需要 Bun PR #25986 提供的 TestReporter WebSocket 事件)
@stryker-mutator/core ^9.6.1
安装
bun add -D @hughescr/stryker-bun-runner @stryker-mutator/core
配置
// stryker.conf.mjs
export default {
testRunner: 'bun',
coverageAnalysis: 'perTest',
mutate: ['src/**/*.ts', '!src/**/*.test.ts'],
thresholds: { high: 80, low: 60, break: 60 },
incremental: true,
bun: {
inspectorTimeout: 5000, // WebSocket Inspector connection timeout (ms)
// bunPath: '/path/to/bun', // only if custom Bun install
// timeout: 30000, // test timeout (ms)
// env: { DEBUG: 'true' },
// bunArgs: ['--bail'],
},
}
关键行为
- 顺序执行:使用
--concurrency=1运行测试,以准确获取每个测试的覆盖情况。速度比并行执行慢,但这是正确关联测试与变异体的必要条件。 - 并发测试修补:在变异测试期间,自动将
describe.concurrent()、test.concurrent()、it.concurrent()修补为顺序运行——无需修改代码。 - Inspector 协议:使用 Bun 的 WebSocket Inspector API 发现测试并关联覆盖情况。
运行 Stryker
# Run mutation testing
bunx stryker run
# Incremental mode (only changed files)
bunx stryker run --incremental
# Specific files
bunx stryker run --mutate "src/utils/**/*.ts"
# Open HTML report
open reports/mutation/html/index.html
示例:较弱的测试
// Source code
function calculateDiscount(price: number, percentage: number): number {
return price - (price * percentage / 100)
}
// ❌ WEAK: Test passes even if we mutate calculation
test('applies discount', () => {
expect(calculateDiscount(100, 10)).toBeDefined() // Too weak!
})
// ✅ STRONG: Test catches mutation
test('applies discount correctly', () => {
expect(calculateDiscount(100, 10)).toBe(90)
expect(calculateDiscount(100, 20)).toBe(80)
expect(calculateDiscount(50, 10)).toBe(45)
})
Python(mutmut)
安装
uv add --dev mutmut
运行 mutmut
# Run mutation testing
uv run mutmut run
# Show results
uv run mutmut results
# Show specific mutant
uv run mutmut show 1
# Generate HTML report
uv run mutmut html
open html/index.html
常见变异类型
// Arithmetic Operator
// Original: a + b → a - b, a * b, a / b
// Relational Operator
// Original: a > b → a >= b, a < b, a <= b
// Logical Operator
// Original: a && b → a || b
// Boolean Literal
// Original: true → false
变异得分目标
| 得分 | 质量 | 行动 |
|---|---|---|
| 90%+ | 优秀 | 保持质量 |
| 80-89% | 良好 | 小幅改进 |
| 70-79% | 可接受 | 针对薄弱环节进行改进 |
| < 60% | 较差 | 需要大幅改进 |
改进较弱的测试
模式:断言不足
// Before: Mutation survives
test('calculates sum', () => {
expect(sum([1, 2, 3])).toBeGreaterThan(0) // Weak!
})
// After: Mutation killed
test('calculates sum correctly', () => {
expect(sum([1, 2, 3])).toBe(6)
expect(sum([0, 0, 0])).toBe(0)
expect(sum([])).toBe(0)
})
模式:边界条件
// After: Tests boundaries
test('validates age boundaries', () => {
expect(isValidAge(18)).toBe(true) // Min valid
expect(isValidAge(17)).toBe(false) // Just below
expect(isValidAge(100)).toBe(true) // Max valid
expect(isValidAge(101)).toBe(false) // Just above
})
最佳实践
- 从核心业务逻辑模块开始
- 在进行变异测试前,确保覆盖率达到 80% 以上
- 增量运行(仅针对已更改的文件)
- 优先关注重要文件
- 不要期望变异得分达到 100%(存在等价变异体)
工作流
# 1. Ensure good coverage first
bun test --coverage
# Target: 80%+ coverage
# 2. Run mutation testing
bunx stryker run
# 3. Check report
open reports/mutation/html/index.html
# 4. Fix survived mutants
# 5. Re-run incrementally
bunx stryker run --incremental
# or: npx stryker run --incremental
另请参阅
vitest-testing- 单元测试框架test-quality-analysis- 检测测试异味
评论
支持 Markdown