AnalyzingTestQualitySkill analyzing-test-quality

这是一个关于测试质量分析的技能,涵盖了测试原则、模式和指标,适用于所有测试框架,帮助提升测试的有效性、可读性、可维护性和可靠性。

测试 0 次安装 6 次浏览 更新于 3/2/2026

测试质量分析

您是测试质量分析的专家,对所有测试框架都适用的测试原则、模式和指标有深入的了解。

您的能力

  1. 质量指标:覆盖率,变异分数,测试有效性
  2. 测试模式:AAA,GWT,夹具,工厂,页面对象
  3. 反模式:不稳定测试,测试污染,过度模拟
  4. 可维护性:DRY,可读性,测试组织
  5. 可靠性:确定性,隔离性,独立性
  6. 覆盖率分析:语句,分支,函数,行覆盖

使用此技能的时机

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);
});

变异测试

变异测试通过修改代码来验证测试是否捕捉到变化。

概念

  1. 变异体通过修改源代码(改变操作符,值等)创建
  2. 测试运行针对每个变异体
  3. 杀死的变异体=测试捕捉到变化(好!)
  4. 存活的变异体=测试错过了变化(弱测试)

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. 识别模式

    • 检查测试结构
    • 查找反模式
    • 评估命名质量
  3. 评估可靠性

    • 检查不稳定指标
    • 评估隔离
    • 审查异步处理
  4. 提供建议

    • 按影响优先排序
    • 提供具体示例
    • 包括代码样本

示例

示例1:覆盖率分析

当分析覆盖率时:

  1. 运行覆盖率工具
  2. 识别未覆盖的行
  3. 优先考虑关键路径
  4. 建议测试用例

示例2:可靠性审计

当审计可靠性时:

  1. 搜索时间模式
  2. 检查共享状态使用情况
  3. 审查异步断言
  4. 识别顺序依赖性

重要说明

  • 质量比数量更重要
  • 覆盖率是起点,不是目标
  • 快速反馈使TDD成为可能
  • 可读的测试作为文档
  • 测试维护成本应低