测试质量分析 test-quality-analysis

测试质量分析技能用于检测和改善软件测试中的问题,如测试异味、过度模拟、不稳定测试和覆盖率不足,通过分析测试有效性、可维护性和可靠性来提升整体测试质量。关键词:测试质量、测试分析、测试异味、过度模拟、不稳定测试、覆盖率。

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

name: test-quality-analysis description: 检测测试异味、过度模拟、不稳定测试和覆盖率问题。分析测试有效性、可维护性和可靠性。在审查测试或改进测试质量时使用。 allowed-tools: Bash, Read, Edit, Write, Grep, Glob, TodoWrite

测试质量分析

用于分析和改进测试质量的专家知识 - 检测测试异味、过度模拟、覆盖率不足和测试反模式。

核心维度

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

测试异味

过度模拟

问题: 模拟过多依赖使测试脆弱。

// ❌ 错误: 过度模拟
test('calculate total', () => {
  const mockAdd = vi.fn(() => 10)
  const mockMultiply = vi.fn(() => 20)
  // 测试实现,而不是行为
})

// ✅ 正确: 仅模拟外部依赖
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、数据库、文件系统)。

脆弱测试

问题: 测试因无关代码更改而中断。

// ❌ 错误: 测试实现细节
await page.locator('.form-container > div:nth-child(2) > button').click()

// ✅ 正确: 语义选择器
await page.getByRole('button', { name: 'Submit' }).click()

不稳定测试

问题: 测试非确定性地通过或失败。

// ❌ 错误: 竞态条件
test('loads data', async () => {
  fetchData()
  await new Promise(resolve => setTimeout(resolve, 1000))
  expect(data).toBeDefined()
})

// ✅ 正确: 适当的异步处理
test('loads data', async () => {
  const data = await fetchData()
  expect(data).toBeDefined()
})

弱断言

// ❌ 错误: 弱断言
test('returns users', async () => {
  const users = await getUsers()
  expect(users).toBeDefined() // 太模糊!
})

// ✅ 正确: 强、具体的断言
test('creates user with correct attributes', async () => {
  const user = await createUser({ name: 'John' })
  expect(user).toMatchObject({
    id: expect.any(Number),
    name: 'John',
  })
})

分析工具

# Vitest 覆盖率(推荐 bun)
bun test --coverage
open coverage/index.html

# 检查阈值
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 () => {
  // 安排
  const userData = { email: 'user@example.com' }

  // 行动
  const user = await registerUser(userData)

  // 断言
  expect(user.email).toBe('user@example.com')
})

代码审查检查清单

  • [ ] 测试验证行为,而不是实现
  • [ ] 断言具体且有意义
  • [ ] 无不稳定测试(定时、排序问题)
  • [ ] 适当的 async/await 使用
  • [ ] 测试名称清晰描述行为
  • [ ] 最小化代码重复
  • [ ] 关键路径有测试
  • [ ] 覆盖快乐路径和错误案例

常见反模式

测试实现细节

// ❌ 错误
const spy = vi.spyOn(Math, 'sqrt')
calculateDistance()
expect(spy).toHaveBeenCalled() // 测试如何,而不是什么

// ✅ 正确
const distance = calculateDistance({ x: 0, y: 0 }, { x: 3, y: 4 })
expect(distance).toBe(5) // 测试输出

模拟过多

// ❌ 错误
const mockAdd = vi.fn((a, b) => a + b)

// ✅ 正确: 使用真实实现
import { add } from './utils'
// 仅模拟外部服务
const mockPaymentGateway = vi.fn()

参见

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