testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<objective> Comprehensive testing skill covering TDD workflow, test pyramid strategy, mocking patterns, and coverage guidance. Framework-agnostic patterns applicable to pytest, vitest, jest, and other testing frameworks.
This skill emphasizes writing tests that provide confidence without becoming maintenance burdens. Tests should be fast, reliable, and focused on behavior rather than implementation details. </objective>
<quick_start> TDD Red-Green-Refactor cycle:
  1. RED: Write a failing test first
    typescript
    test('adds numbers', () => {
      expect(add(1, 2)).toBe(3);  // Fails - add() doesn't exist
    });
  2. GREEN: Write minimum code to pass
    typescript
    const add = (a, b) => a + b;  // Test passes
  3. REFACTOR: Clean up while tests stay green
Test pyramid: 70% unit, 25% integration, 5% E2E </quick_start>
<success_criteria> Testing is successful when:
  • TDD cycle followed: test written before implementation code
  • Test pyramid balanced: ~70% unit, ~25% integration, ~5% E2E
  • Tests are independent and can run in any order
  • No flaky tests (run 3x to verify reliability)
  • Coverage meets targets: 70-80% lines, 100% critical paths
  • Test names describe behavior (what + when + expected result)
  • Mocks only used for external dependencies, not own code </success_criteria>
<core_principles>
<objective> 涵盖TDD工作流、测试金字塔策略、Mocking模式和覆盖率指导的全面测试技能。与框架无关的模式适用于pytest、vitest、jest及其他测试框架。
本技能强调编写能提供可信度且不会成为维护负担的测试。测试应快速、可靠,并聚焦于行为而非实现细节。 </objective>
<quick_start> TDD红-绿-重构循环:
  1. 红(RED):先编写失败的测试
    typescript
    test('adds numbers', () => {
      expect(add(1, 2)).toBe(3);  // 失败 - add() 不存在
    });
  2. 绿(GREEN):编写刚好能通过测试的代码
    typescript
    const add = (a, b) => a + b;  // 测试通过
  3. 重构(REFACTOR):在测试保持通过的前提下优化代码
测试金字塔:70%单元测试,25%集成测试,5%E2E测试 </quick_start>
<success_criteria> 测试成功的标准:
  • 遵循TDD循环:在实现代码前编写测试
  • 测试金字塔比例均衡:约70%单元测试、25%集成测试、5%E2E测试
  • 测试相互独立,可按任意顺序运行
  • 无不稳定测试(运行3次以验证可靠性)
  • 覆盖率达到目标:70-80%行覆盖率,100%关键路径覆盖率
  • 测试名称描述行为(内容+场景+预期结果)
  • 仅对外部依赖使用Mock,不对自有代码使用 </success_criteria>
<core_principles>

The Testing Mindset

测试思维

  1. Tests are documentation - A failing test is a specification that hasn't been implemented
  2. Test behavior, not implementation - Tests should survive refactoring
  3. Fast feedback loops - Unit tests run in milliseconds, not seconds
  4. Isolation by default - Each test should be independent
  5. Arrange-Act-Assert - Clear structure in every test </core_principles>
<tdd_workflow>
  1. 测试即文档 - 失败的测试就是尚未实现的需求规格
  2. 测试行为而非实现 - 测试应能在代码重构后依然有效
  3. 快速反馈循环 - 单元测试应在毫秒级运行,而非秒级
  4. 默认隔离性 - 每个测试应相互独立
  5. 准备-执行-断言(Arrange-Act-Assert) - 每个测试都应具备清晰的结构 </core_principles>
<tdd_workflow>

TDD: Red-Green-Refactor

TDD:红-绿-重构

┌─────────────────────────────────────────────────────────┐
│                    TDD CYCLE                             │
│                                                          │
│    ┌─────────┐                                          │
│    │   RED   │ ◄─── Write a failing test                │
│    └────┬────┘                                          │
│         │                                                │
│         ▼                                                │
│    ┌─────────┐                                          │
│    │  GREEN  │ ◄─── Write minimum code to pass          │
│    └────┬────┘                                          │
│         │                                                │
│         ▼                                                │
│    ┌─────────┐                                          │
│    │REFACTOR │ ◄─── Clean up while tests stay green     │
│    └────┬────┘                                          │
│         │                                                │
│         └──────────────► Back to RED                    │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│                    TDD 循环                             │
│                                                          │
│    ┌─────────┐                                          │
│    │   RED   │ ◄─── 编写一个失败的测试                │
│    └────┬────┘                                          │
│         │                                                │
│         ▼                                                │
│    ┌─────────┐                                          │
│    │  GREEN  │ ◄─── 编写刚好能通过测试的代码          │
│    └────┬────┘                                          │
│         │                                                │
│         ▼                                                │
│    ┌─────────┐                                          │
│    │REFACTOR │ ◄─── 在测试保持通过的前提下优化代码     │
│    └────┬────┘                                          │
│         │                                                │
│         └──────────────► 返回至 RED 阶段                    │
└─────────────────────────────────────────────────────────┘

The Rules

规则

  1. Write a failing test first - Never write production code without a failing test
  2. Write only enough test to fail - Compilation failures count as failures
  3. Write only enough code to pass - No more, no less
  4. Refactor only when green - Never refactor with failing tests
  1. 先编写失败的测试 - 永远不要在没有失败测试的情况下编写生产代码
  2. 只编写刚好会失败的测试 - 编译错误也属于失败
  3. 只编写刚好能通过测试的代码 - 不多不少
  4. 仅在测试通过时重构 - 永远不要在测试失败时进行重构

Common TDD Mistakes

常见TDD错误

MistakeWhy It's WrongInstead
Writing tests after codeTests become confirmation biasRed-Green-Refactor
Testing private methodsTests implementation, not behaviorTest public interface
Big leaps in test complexityHard to debug failuresBaby steps
Skipping refactor stepTechnical debt accumulatesAlways clean up
</tdd_workflow>
<test_pyramid>
错误行为问题所在正确做法
在编写代码后才写测试测试会变成确认偏差遵循红-绿-重构循环
测试私有方法测试的是实现而非行为测试公共接口
测试复杂度大幅跃升难以调试失败原因小步迭代
跳过重构步骤技术债务不断累积始终进行代码清理
</tdd_workflow>
<test_pyramid>

The Test Pyramid

测试金字塔

                    ┌───────────┐
                    │    E2E    │  Few, slow, expensive
                    │   Tests   │  (minutes)
                    └─────┬─────┘
               ┌──────────┴──────────┐
               │   Integration Tests  │  Some, medium speed
               │   (API, Database)    │  (seconds)
               └──────────┬───────────┘
        ┌─────────────────┴─────────────────┐
        │          Unit Tests                │  Many, fast, cheap
        │    (Functions, Components)         │  (milliseconds)
        └────────────────────────────────────┘
                    ┌───────────┐
                    │    E2E    │  数量少、速度慢、成本高
                    │   测试    │  (分钟级)
                    └─────┬─────┘
               ┌──────────┴──────────┐
               │   集成测试  │  数量中等、速度中等
               │  (API、数据库)    │  (秒级)
               └──────────┬───────────┘
        ┌─────────────────┴─────────────────┐
        │          单元测试                │  数量多、速度快、成本低
        │   (函数、组件)         │  (毫秒级)
        └────────────────────────────────────┘

Distribution Guidelines

分布指南

TypePercentageSpeedScope
Unit70-80%<10ms eachSingle function/component
Integration15-25%<1s eachMultiple components, DB
E2E5-10%<30s eachFull user flows
类型占比速度范围
单元测试70-80%每个<10ms单个函数/组件
集成测试15-25%每个<1s多个组件、数据库
E2E测试5-10%每个<30s完整用户流程

What to Test Where

不同测试类型的适用场景

Unit Tests:
  • Pure functions
  • Business logic
  • Data transformations
  • Validation rules
  • Component rendering
Integration Tests:
  • API endpoints
  • Database operations
  • Service interactions
  • Component integration
E2E Tests:
  • Critical user flows (login, checkout)
  • Happy paths only
  • Smoke tests </test_pyramid>
<when_to_mock>
单元测试:
  • 纯函数
  • 业务逻辑
  • 数据转换
  • 验证规则
  • 组件渲染
集成测试:
  • API接口
  • 数据库操作
  • 服务间交互
  • 组件集成
E2E测试:
  • 关键用户流程(登录、结账)
  • 仅测试主流程
  • 冒烟测试 </test_pyramid>
<when_to_mock>

Mocking Strategy

Mock策略

The London vs Detroit Schools

伦敦学派 vs 底特律学派

London School (Mockist):
  • Mock all dependencies
  • Test in complete isolation
  • Tests are very focused
Detroit School (Classicist):
  • Only mock external services
  • Test natural units together
  • Tests are more realistic
Recommended: Pragmatic approach
  • Mock external services (APIs, DBs in unit tests)
  • Don't mock your own code unless necessary
  • Use real implementations in integration tests
伦敦学派(Mockist):
  • Mock所有依赖
  • 完全隔离测试
  • 测试聚焦性极强
底特律学派(Classicist):
  • 仅Mock外部服务
  • 测试天然的单元组合
  • 测试更贴近真实场景
推荐:务实的方法
  • 在单元测试中Mock外部服务(API、数据库)
  • 除非必要,否则不要Mock自有代码
  • 在集成测试中使用真实实现

What to Mock

Mock的适用与不适用场景

MockDon't Mock
External APIsYour own pure functions
File system (in unit tests)Data transformations
Network requestsBusiness logic
Time/randomnessIn-memory data structures
Expensive computationsSimple utilities
可Mock的对象不可Mock的对象
外部API自有纯函数
文件系统(单元测试中)数据转换逻辑
网络请求业务逻辑
时间/随机数内存数据结构
高耗时计算简单工具函数

Mocking Patterns

Mock模式示例

typescript
// GOOD: Mock external dependency
const mockFetch = vi.fn().mockResolvedValue({ data: [] });

// BAD: Mocking your own utilities
const mockFormatDate = vi.fn(); // Don't do this

// GOOD: Dependency injection for testability
function createService(httpClient = fetch) {
  return {
    getData: () => httpClient('/api/data')
  };
}

// In test:
const mockClient = vi.fn();
const service = createService(mockClient);
</when_to_mock>
<test_structure>
typescript
// 正确:Mock外部依赖
const mockFetch = vi.fn().mockResolvedValue({ data: [] });

// 错误:Mock自有工具函数
const mockFormatDate = vi.fn(); // 不要这样做

// 正确:通过依赖注入提升可测试性
function createService(httpClient = fetch) {
  return {
    getData: () => httpClient('/api/data')
  };
}

// 测试中:
const mockClient = vi.fn();
const service = createService(mockClient);
</when_to_mock>
<test_structure>

Test Organization

测试组织

File Naming

文件命名规范

src/
├── components/
│   ├── Button.tsx
│   └── Button.test.tsx      # Colocated test
├── utils/
│   ├── format.ts
│   └── format.test.ts
└── __tests__/               # Or separate folder
    └── integration/
        └── api.test.ts
src/
├── components/
│   ├── Button.tsx
│   └── Button.test.tsx      // 与源代码同目录的测试文件
├── utils/
│   ├── format.ts
│   └── format.test.ts
└── __tests__/               // 或者单独的测试文件夹
    └── integration/
        └── api.test.ts

Test Naming

测试命名规范

typescript
// Pattern: describe what + when + expected result
describe('UserService', () => {
  describe('createUser', () => {
    it('returns user object when given valid email', () => {});
    it('throws ValidationError when email is invalid', () => {});
    it('sends welcome email after successful creation', () => {});
  });
});

// Alternative: BDD style
describe('UserService', () => {
  describe('when creating a user with valid data', () => {
    it('should return the created user', () => {});
    it('should send a welcome email', () => {});
  });

  describe('when email is invalid', () => {
    it('should throw ValidationError', () => {});
  });
});
typescript
// 模式:描述(内容+场景+预期结果)
describe('UserService', () => {
  describe('createUser', () => {
    it('传入有效邮箱时返回用户对象', () => {});
    it('传入无效邮箱时抛出ValidationError', () => {});
    it('创建成功后发送欢迎邮件', () => {});
  });
});

// 替代方案:BDD风格
describe('UserService', () => {
  describe('当传入有效数据创建用户时', () => {
    it('应返回创建好的用户', () => {});
    it('应发送欢迎邮件', () => {});
  });

  describe('当邮箱无效时', () => {
    it('应抛出ValidationError', () => {});
  });
});

Arrange-Act-Assert

准备-执行-断言(Arrange-Act-Assert)结构

typescript
it('calculates total with discount', () => {
  // Arrange - set up test data
  const cart = createCart([
    { price: 100, quantity: 2 },
    { price: 50, quantity: 1 }
  ]);
  const discount = 0.1;

  // Act - perform the action
  const total = calculateTotal(cart, discount);

  // Assert - verify result
  expect(total).toBe(225); // (200 + 50) * 0.9
});
</test_structure>
<what_not_to_test>
typescript
it('计算应用折扣后的总价', () => {
  // 准备 - 设置测试数据
  const cart = createCart([
    { price: 100, quantity: 2 },
    { price: 50, quantity: 1 }
  ]);
  const discount = 0.1;

  // 执行 - 执行待测试的操作
  const total = calculateTotal(cart, discount);

  // 断言 - 验证结果
  expect(total).toBe(225); // (200 + 50) * 0.9
});
</test_structure>
<what_not_to_test>

What NOT to Test

无需测试的内容

Skip These

跳过以下内容

  1. Framework code - React's useState, Express routing
  2. Third-party libraries - They have their own tests
  3. Trivial getters/setters - No logic = no test needed
  4. Implementation details - Private methods, internal state
  5. One-line functions - Unless they have complex logic
  1. 框架代码 - React的useState、Express路由
  2. 第三方库 - 它们已有自己的测试
  3. 简单的getter/setter - 无逻辑则无需测试
  4. 实现细节 - 私有方法、内部状态
  5. 单行函数 - 除非包含复杂逻辑

Focus On

聚焦测试以下内容

  1. Business logic - Where bugs hide
  2. Edge cases - Nulls, empty arrays, boundaries
  3. Error paths - What happens when things fail
  4. User-facing behavior - What users actually do
  5. Regressions - Bugs that came back once
  1. 业务逻辑 - 漏洞常出现在此处
  2. 边界情况 - null值、空数组、临界值
  3. 错误路径 - 出现故障时的处理逻辑
  4. 用户可见行为 - 用户实际会操作的内容
  5. 回归问题 - 曾经出现过并修复的漏洞

Coverage Targets

覆盖率目标

MetricTargetNotes
Line coverage70-80%Higher isn't always better
Branch coverage70-80%More important than lines
Critical paths100%Auth, payments, data mutations
Warning: 100% coverage doesn't mean good tests. Bad tests can hit every line without testing anything meaningful. </what_not_to_test>
<references> For detailed patterns, load the appropriate reference:
TopicReference FileWhen to Load
Unit testing patterns
reference/unit-testing.md
Writing unit tests, mocking
Integration testing
reference/integration-testing.md
API tests, database tests
Test organization
reference/test-organization.md
Structuring test suites
Coverage strategies
reference/coverage-strategies.md
Setting coverage goals
To load: Ask for the specific topic or check if context suggests it. </references>
<framework_patterns>
指标目标说明
行覆盖率70-80%并非越高越好
分支覆盖率70-80%比行覆盖率更重要
关键路径100%认证、支付、数据变更等流程
警告: 100%覆盖率并不代表测试质量高。糟糕的测试可以覆盖每一行代码,但实际没有测试任何有意义的内容。 </what_not_to_test>
<references> 如需了解详细模式,请加载对应的参考文档:
主题参考文件加载时机
单元测试模式
reference/unit-testing.md
编写单元测试、Mock时
集成测试
reference/integration-testing.md
API测试、数据库测试时
测试组织
reference/test-organization.md
构建测试套件结构时
覆盖率策略
reference/coverage-strategies.md
设置覆盖率目标时
加载方式: 请求特定主题,或根据上下文判断是否需要加载。 </references>
<framework_patterns>

Quick Reference by Framework

各框架快速参考

pytest (Python)

pytest(Python)

python
undefined
python
undefined

Fixtures

夹具(Fixtures)

@pytest.fixture def user(): return User(name="test")
def test_user_greet(user): assert user.greet() == "Hello, test"
@pytest.fixture def user(): return User(name="test")
def test_user_greet(user): assert user.greet() == "Hello, test"

Parametrize

参数化测试

@pytest.mark.parametrize("input,expected", [ ("hello", "HELLO"), ("world", "WORLD"), ]) def test_uppercase(input, expected): assert uppercase(input) == expected
undefined
@pytest.mark.parametrize("input,expected", [ ("hello", "HELLO"), ("world", "WORLD"), ]) def test_uppercase(input, expected): assert uppercase(input) == expected
undefined

vitest/jest (TypeScript)

vitest/jest(TypeScript)

typescript
// Basic test
test('adds numbers', () => {
  expect(add(1, 2)).toBe(3);
});

// Mock
vi.mock('./api', () => ({
  fetchUser: vi.fn().mockResolvedValue({ name: 'test' })
}));

// Component test
import { render, screen } from '@testing-library/react';

test('renders button', () => {
  render(<Button>Click</Button>);
  expect(screen.getByRole('button')).toHaveTextContent('Click');
});
typescript
// 基础测试
test('adds numbers', () => {
  expect(add(1, 2)).toBe(3);
});

// Mock示例
vi.mock('./api', () => ({
  fetchUser: vi.fn().mockResolvedValue({ name: 'test' })
}));

// 组件测试
import { render, screen } from '@testing-library/react';

test('渲染按钮', () => {
  render(<Button>Click</Button>);
  expect(screen.getByRole('button')).toHaveTextContent('Click');
});

Testing Library Principles

Testing Library 原则

  1. Query by role, not test ID
  2. Test what users see, not implementation
  3. Prefer
    userEvent
    over
    fireEvent
  4. Avoid testing internal state </framework_patterns>
<checklist>
  1. 按角色查询元素,而非测试ID
  2. 测试用户可见的内容,而非实现细节
  3. 优先使用
    userEvent
    而非
    fireEvent
  4. 避免测试内部状态 </framework_patterns>
<checklist>

Testing Checklist

测试检查清单

Before marking code complete:
  • Unit tests cover happy path
  • Unit tests cover error cases
  • Edge cases tested (null, empty, boundary)
  • Integration tests for API endpoints
  • No flaky tests (run 3x to verify)
  • Tests are independent (run in any order)
  • Test names describe behavior
  • No hardcoded timeouts (use waitFor)
  • Mocks are reset between tests
  • Coverage meets project standards
</checklist>
在标记代码完成前,请确认:
  • 单元测试覆盖了主流程
  • 单元测试覆盖了错误场景
  • 边界情况已测试(null、空值、临界值)
  • API接口已有集成测试
  • 无不稳定测试(运行3次验证)
  • 测试相互独立(可按任意顺序运行)
  • 测试名称描述了行为
  • 无硬编码超时(使用waitFor)
  • Mock在测试间已重置
  • 覆盖率符合项目标准
</checklist>