test-quality-analysis

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Test Quality Analysis

测试质量分析

Expert knowledge for analyzing and improving test quality - detecting test smells, overmocking, insufficient coverage, and testing anti-patterns.
用于分析和提升测试质量的专业知识——检测测试异味、过度Mock、覆盖率不足以及测试反模式。

Core Dimensions

核心维度

  • Correctness: Tests verify the right behavior
  • Reliability: Tests are deterministic, not flaky
  • Maintainability: Tests are easy to understand
  • Performance: Tests run quickly
  • Coverage: Tests cover critical code paths
  • Isolation: Tests don't depend on external state
  • 正确性:测试验证正确的行为
  • 可靠性:测试结果确定,不存在不稳定情况
  • 可维护性:测试易于理解
  • 性能:测试运行速度快
  • 覆盖率:测试覆盖关键代码路径
  • 隔离性:测试不依赖外部状态

Test Smells

测试异味

Overmocking

过度Mock

Problem: Mocking too many dependencies makes tests fragile.
typescript
// ❌ 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)
})
Detection: More than 3-4 mocks, mocking pure functions, complex mock setup.
Fix: Mock only I/O boundaries (APIs, databases, filesystem).
问题:过多Mock依赖会导致测试脆弱。
typescript
// ❌ 不良示例:过度Mock
test('calculate total', () => {
  const mockAdd = vi.fn(() => 10)
  const mockMultiply = vi.fn(() => 20)
  // 测试实现细节,而非行为
})

// ✅ 良好示例:仅Mock外部依赖
test('calculate order total', () => {
  const mockPricingAPI = vi.fn(() => ({ tax: 0.1 }))
  const total = calculateTotal(order, mockPricingAPI)
  expect(total).toBe(38)
})
检测方式:存在3-4个以上的Mock、Mock纯函数、复杂的Mock配置。
修复方案:仅Mock I/O边界(API、数据库、文件系统)。

Fragile Tests

脆弱测试

Problem: Tests break with unrelated code changes.
typescript
// ❌ 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()
问题:无关代码变更会导致测试失败。
typescript
// ❌ 不良示例:测试实现细节
await page.locator('.form-container > div:nth-child(2) > button').click()

// ✅ 良好示例:语义化选择器
await page.getByRole('button', { name: 'Submit' }).click()

Flaky Tests

不稳定测试

Problem: Tests pass or fail non-deterministically.
typescript
// ❌ 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()
})
问题:测试结果随机,时而通过时而失败。
typescript
// ❌ 不良示例:竞态条件
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()
})

Poor Assertions

断言质量低下

typescript
// ❌ 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',
  })
})
typescript
// ❌ 不良示例:模糊断言
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',
  })
})

Analysis Tools

分析工具

bash
undefined
bash
undefined

Vitest coverage (prefer bun)

Vitest 覆盖率统计(推荐使用 bun)

bun test --coverage open coverage/index.html
bun test --coverage open coverage/index.html

Check thresholds

检查阈值

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

pytest-cov (Python)

pytest-cov(Python)

uv run pytest --cov --cov-report=html open htmlcov/index.html
undefined
uv run pytest --cov --cov-report=html open htmlcov/index.html
undefined

Best Practices Checklist

最佳实践检查清单

Unit Test Quality (FIRST)

单元测试质量(FIRST原则)

  • Fast: Tests run in milliseconds
  • Isolated: No dependencies between tests
  • Repeatable: Same results every time
  • Self-validating: Clear pass/fail
  • Timely: Written alongside code
  • 快速:测试以毫秒级运行
  • 隔离:测试之间无依赖
  • 可重复:每次结果一致
  • 自验证:通过/失败结果明确
  • 及时:与代码同步编写

Mock Guidelines

Mock 指南

  • Mock only external dependencies
  • Don't mock business logic or pure functions
  • Use real implementations when possible
  • Limit to 3-4 mocks per test maximum
  • 仅Mock外部依赖
  • 不要Mock业务逻辑或纯函数
  • 尽可能使用真实实现
  • 每个测试最多使用3-4个Mock

Coverage Goals

覆盖率目标

  • 80%+ line coverage for business logic
  • 100% for critical paths (auth, payment)
  • All error paths tested
  • Boundary conditions tested
  • 业务逻辑行覆盖率达到80%以上
  • 关键路径(认证、支付)覆盖率100%
  • 所有错误路径均有测试覆盖
  • 边界条件均有测试覆盖

Test Structure (AAA Pattern)

测试结构(AAA模式)

typescript
test('user registration', async () => {
  // Arrange
  const userData = { email: 'user@example.com' }

  // Act
  const user = await registerUser(userData)

  // Assert
  expect(user.email).toBe('user@example.com')
})
typescript
test('user registration', async () => {
  // 准备
  const userData = { email: 'user@example.com' }

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

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

Code Review Checklist

代码评审检查清单

  • Tests verify behavior, not implementation
  • Assertions are specific and meaningful
  • No flaky tests (timing, ordering issues)
  • Proper async/await usage
  • Test names clearly describe behavior
  • Minimal code duplication
  • Critical paths have tests
  • Both happy path and error cases covered
  • 测试验证行为而非实现细节
  • 断言具体且有意义
  • 不存在不稳定测试(时序、顺序问题)
  • 正确使用 async/await
  • 测试名称清晰描述行为
  • 代码重复率低
  • 关键路径有测试覆盖
  • 覆盖正常流程和异常场景

Common Anti-Patterns

常见反模式

Testing Implementation Details

测试实现细节

typescript
// ❌ 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
typescript
// ❌ 不良示例
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) // 测试输出结果

Mocking Too Much

过度Mock

typescript
// ❌ 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()
typescript
// ❌ 不良示例
const mockAdd = vi.fn((a, b) => a + b)

// ✅ 良好示例:使用真实实现
import { add } from './utils'
// 仅Mock外部服务
const mockPaymentGateway = vi.fn()

See Also

相关链接

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