测试质量分析
您是测试质量分析的专家,对所有测试框架都适用的测试原则、模式和指标有深入的了解。
您的能力
- 质量指标:覆盖率,变异分数,测试有效性
- 测试模式:AAA,GWT,夹具,工厂,页面对象
- 反模式:不稳定测试,测试污染,过度模拟
- 可维护性:DRY,可读性,测试组织
- 可靠性:确定性,隔离性,独立性
- 覆盖率分析:语句,分支,函数,行覆盖
使用此技能的时机
Claude应该在以下情况下自动调用此技能:
- 用户询问测试质量或测试有效性
- 讨论代码覆盖率报告或指标
- 提及测试可靠性或不稳定情况
- 需要测试组织或重构
- 请求一般测试改进
如何使用此技能
访问资源
使用{baseDir}引用此技能目录中的文件:
- 脚本:
{baseDir}/scripts/ - 文档:
{baseDir}/references/ - 模板:
{baseDir}/assets/
可用资源
此技能包括{baseDir}中的即用资源:
- references/quality-checklist.md - 可打印的测试质量检查清单及评分指南
- assets/quality-report.template.md - 完整的测试质量评估报告模板
- scripts/calculate-metrics.sh - 计算测试指标(测试计数,比率,模式,断言)
测试质量维度
1. 正确性
测试准确验证预期行为:
- 测试符合需求
- 断言完整
- 覆盖边缘情况
- 测试错误场景
2. 可读性
测试易于理解:
- 清晰的命名(测试内容)
- 适当的结构(AAA/GWT模式)
- 最小的设置噪声
- 自文档化代码
3. 可维护性
测试易于修改:
- DRY与适当的助手
- 专注的测试(单一责任)
- 适当的抽象级别
- 清晰的依赖关系
4. 可靠性
测试产生一致的结果:
- 无时间依赖性
- 适当的隔离
- 确定性数据
- 独立执行
5. 速度
测试高效运行:
- 适当的测试金字塔
- 有效的设置/拆除
- 适当的模拟策略
- 并行执行
测试质量检查清单
结构
- [ ] 使用AAA(Arrange-Act-Assert)或GWT模式
- [ ] 每个测试一个逻辑断言
- [ ] 描述性测试名称
- [ ] 适当的describe/context嵌套
- [ ] 适当的设置/拆除
覆盖率
- [ ] 快乐路径场景
- [ ] 错误/边缘情况
- [ ] 边界条件
- [ ] 集成点
- [ ] 安全场景
可靠性
- [ ] 无时间依赖性
- [ ] 适当的异步处理
- [ ] 隔离测试(无共享状态)
- [ ] 确定性数据
- [ ] 顺序独立
可维护性
- [ ] 可重用的夹具/工厂
- [ ] 清晰的变量命名
- [ ] 专注的断言
- [ ] 适当的抽象
- [ ] 无魔术数字/字符串
常见反模式
测试污染
// BAD: 共享可变状态
let count = 0;
beforeEach(() => count++);
// GOOD: 在设置中重置
let count: number;
beforeEach(() => { count = 0; });
过度模拟
过度模拟隐藏错误并使测试变得脆弱。
// BAD: 模拟一切 - 测试只验证模拟
// Jest
jest.mock('./dep1');
jest.mock('./dep2');
jest.mock('./dep3');
// Vitest
vi.mock('./dep1');
vi.mock('./dep2');
vi.mock('./dep3');
// GOOD: 只模拟边界
// 模拟外部服务,保持内部逻辑真实
mock('./api'); // 仅外部服务
// 测试实际业务逻辑
不稳定断言
// BAD: 时间依赖
await delay(100);
expect(element).toBeVisible();
// GOOD: 等待条件
// 测试库
await waitFor(() => expect(element).toBeVisible());
// Playwright
await expect(element).toBeVisible();
神秘嘉宾
// BAD: 隐藏依赖
test('should process', () => {
const result = process(); // 使用全局数据
expect(result).toBe(42);
});
// GOOD: 明确的设置
test('should process input', () => {
const input = createInput({ value: 21 });
const result = process(input);
expect(result).toBe(42);
});
断言轮盘赌
// BAD: 多个不相关的断言
test('should work', () => {
expect(user.name).toBe('John');
expect(items.length).toBe(3);
expect(total).toBe(100);
});
// GOOD: 专注的断言
test('should set user name', () => {
expect(user.name).toBe('John');
});
test('should have correct item count', () => {
expect(items).toHaveLength(3);
});
变异测试
变异测试通过修改代码来验证测试是否捕捉到变化。
概念
- 变异体通过修改源代码(改变操作符,值等)创建
- 测试运行针对每个变异体
- 杀死的变异体=测试捕捉到变化(好!)
- 存活的变异体=测试错过了变化(弱测试)
Stryker设置
# 安装Stryker
npm install -D @stryker-mutator/core
# 针对特定框架
npm install -D @stryker-mutator/jest-runner # Jest
npm install -D @stryker-mutator/vitest-runner # Vitest
npm install -D @stryker-mutator/mocha-runner # Mocha
# 初始化配置
npx stryker init
Stryker配置
// stryker.conf.js
module.exports = {
packageManager: 'npm',
reporters: ['html', 'clear-text', 'progress'],
testRunner: 'jest',
coverageAnalysis: 'perTest',
// 要变异的内容
mutate: [
'src/**/*.ts',
'!src/**/*.test.ts',
'!src/**/*.spec.ts',
],
// 使用的变异类型
mutator: {
excludedMutations: [
'StringLiteral', // 跳过字符串变异
],
},
// 阈值
thresholds: {
high: 80,
low: 60,
break: 50, // 如果低于此值,则CI失败
},
};
解读结果
变异分数:85%
杀死:170 | 存活:30 | 超时:5 | 无覆盖:10
高分数(>80%):测试有效 中等分数(60-80%):一些薄弱区域 低分数(<60%):测试需要显著改进
常见存活变异
边界变异:<更改为<=
// 如果测试不检查边界,则变异存活
if (value < 10) { ... } // 更改为:value <= 10
算术变异:+更改为-
// 如果结果没有精确检查,则变异存活
return a + b; // 更改为:a - b
布尔变异:&&更改为||
// 如果两个条件都没有测试,则变异存活
if (a && b) { ... } // 更改为:a || b
CI集成
# GitHub Actions
- name: 运行变异测试
run: npx stryker run
- name: 上传Stryker报告
uses: actions/upload-artifact@v3
with:
name: stryker-report
path: reports/mutation/
覆盖率指标
覆盖率类型
- 语句:执行的行
- 分支:采取的决策路径
- 函数:调用的函数
- 行:覆盖的行
覆盖率阈值
// 推荐的最低值
{
statements: 80,
branches: 75,
functions: 80,
lines: 80
}
覆盖率陷阱
- 高覆盖率≠好的测试
- 可能会错过逻辑错误
- 不测试交互
- 可能会激励糟糕的测试
变异测试
概念
变异测试修改代码以检查测试是否捕捉到变化:
- 测试应在代码变异时失败
- 存活的变异体表明测试薄弱
- 更高的杀死率=更好的测试
变异类型
- 算术运算符(+,-,*,/)
- 比较运算符(<,>,==)
- 布尔运算符(&&,||,!)
- 返回值
- 常量
测试金字塔
单元测试(基础)
- 快速执行
- 隔离组件
- 高覆盖率
- 许多测试
集成测试(中间)
- 组件交互
- 数据库/API调用
- 中等覆盖率
- 中等数量
E2E测试(顶部)
- 完整用户流程
- 真实浏览器
- 关键路径仅
- 少数测试
分析工作流程
当分析测试质量时:
-
收集指标
- 运行覆盖率工具
- 计算测试/代码比率
- 测量测试执行时间
-
识别模式
- 检查测试结构
- 查找反模式
- 评估命名质量
-
评估可靠性
- 检查不稳定指标
- 评估隔离
- 审查异步处理
-
提供建议
- 按影响优先排序
- 提供具体示例
- 包括代码样本
示例
示例1:覆盖率分析
当分析覆盖率时:
- 运行覆盖率工具
- 识别未覆盖的行
- 优先考虑关键路径
- 建议测试用例
示例2:可靠性审计
当审计可靠性时:
- 搜索时间模式
- 检查共享状态使用情况
- 审查异步断言
- 识别顺序依赖性
重要说明
- 质量比数量更重要
- 覆盖率是起点,不是目标
- 快速反馈使TDD成为可能
- 可读的测试作为文档
- 测试维护成本应低