mcpField
T
test-quality-analysis

test-quality-analysis

github.com/secondsky测试与质量240455 次安装5 周前更新
代码仓库

关于 test-quality-analysis

面向审查和改进测试套件的测试质量分析技能,说明如何识别测试异味与反模式,并提供可靠性、断言、模拟、覆盖率和可维护性方面的建议。

主要功能

  1. 01识别过度模拟与测试异味
  2. 02发现不稳定和脆弱的测试
  3. 03提高断言的明确性
  4. 04检查覆盖率与关键路径
  5. 05遵循模拟和单元测试指南
  6. 06使用代码审查清单

适用场景

  1. 01审查测试套件中的过度模拟
  2. 02排查间歇性失败的测试
  3. 03加强断言和错误路径覆盖
  4. 04在代码审查中评估测试质量

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/secondsky/claude-skills --skill test-quality-analysis

安装后在 Claude Code 中输入/test-quality-analysis确认已加载。

详细介绍

测试质量分析

用于分析和提升测试质量的专业知识——检测测试异味、过度模拟、覆盖不足和测试反模式。

核心维度

  • 正确性:测试验证正确的行为
  • 可靠性:测试具有确定性,不会不稳定
  • 可维护性:测试易于理解
  • 性能:测试运行迅速
  • 覆盖率:测试覆盖关键代码路径
  • 隔离性:测试不依赖外部状态

测试异味

过度模拟

问题:模拟过多依赖项会让测试变得脆弱。

// ❌ BAD: Overmocked
test('calculate total', () => {
  const mockAdd = vi.fn(() => 10)
  const mockMultiply = vi.fn(() => 20)
  // Testing implementation, not behavior
})

// ✅ GOOD: Mock only external dependencies
test('calculate order total', () => {
  const mockPricingAPI = vi.fn(() => ({ tax: 0.1 }))
  const total = calculateTotal(order, mockPricingAPI)
  expect(total).toBe(38)
})

检测:模拟对象超过 3–4 个、模拟纯函数、模拟设置复杂。

解决方法:只模拟 I/O 边界(API、数据库、文件系统)。

脆弱的测试

问题:无关的代码变更会导致测试失败。

// ❌ BAD: Tests implementation details
await page.locator('.form-container > div:nth-child(2) > button').click()

// ✅ GOOD: Semantic selector
await page.getByRole('button', { name: 'Submit' }).click()
不稳定的测试

问题:测试结果不确定,时而通过,时而失败。

// ❌ BAD: Race condition
test('loads data', async () => {
  fetchData()
  await new Promise(resolve => setTimeout(resolve, 1000))
  expect(data).toBeDefined()
})

// ✅ GOOD: Proper async handling
test('loads data', async () => {
  const data = await fetchData()
  expect(data).toBeDefined()
})
断言质量差
// ❌ BAD: Weak assertion
test('returns users', async () => {
  const users = await getUsers()
  expect(users).toBeDefined() // Too vague!
})

// ✅ GOOD: Strong, specific assertions
test('creates user with correct attributes', async () => {
  const user = await createUser({ name: 'John' })
  expect(user).toMatchObject({
    id: expect.any(Number),
    name: 'John',
  })
})

分析工具

# Vitest coverage (prefer bun)
bun test --coverage
open coverage/index.html

# Check thresholds
bun test --coverage --coverage.thresholds.lines=80

# pytest-cov (Python)
uv run pytest --cov --cov-report=html
open htmlcov/index.html

最佳实践检查清单

单元测试质量(FIRST)
  • 快速:测试在毫秒内运行完成
  • 隔离:测试之间没有依赖关系
  • 可重复:每次都得到相同结果
  • 自验证:通过或失败的结果清晰明确
  • 及时:与代码同步编写
模拟指南
  • 只模拟外部依赖项
  • 不要模拟业务逻辑或纯函数
  • 尽可能使用真实实现
  • 每个测试最多使用 3–4 个模拟对象
覆盖率目标
  • 业务逻辑的行覆盖率达到 80% 以上
  • 关键路径(身份验证、支付)的覆盖率达到 100%
  • 测试所有错误路径
  • 测试边界条件
测试结构(AAA 模式)
test('user registration', async () => {
  // Arrange
  const userData = { email: '[email protected]' }

  // Act
  const user = await registerUser(userData)

  // Assert
  expect(user.email).toBe('[email protected]')
})

代码审查检查清单

  • 测试验证的是行为,而非实现
  • 断言具体且有意义
  • 没有不稳定的测试(时序、顺序问题)
  • 正确使用 async/await
  • 测试名称清晰描述行为
  • 代码重复最少
  • 关键路径有测试覆盖
  • 正常情况和错误情况均有覆盖

常见反模式

测试实现细节
// ❌ BAD
const spy = vi.spyOn(Math, 'sqrt')
calculateDistance()
expect(spy).toHaveBeenCalled() // Testing how, not what

// ✅ GOOD
const distance = calculateDistance({ x: 0, y: 0 }, { x: 3, y: 4 })
expect(distance).toBe(5) // Testing output
模拟过多
// ❌ BAD
const mockAdd = vi.fn((a, b) => a + b)

// ✅ GOOD: Use real implementations
import { add } from './utils'
// Only mock external services
const mockPaymentGateway = vi.fn()

另请参阅

  • vitest-testing - TypeScript/JavaScript 测试
  • playwright-testing - 端到端测试
  • mutation-testing - 验证测试有效性

评论

支持 Markdown