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:
-
RED: Write a failing test firsttypescript
test('adds numbers', () => { expect(add(1, 2)).toBe(3); // Fails - add() doesn't exist }); -
GREEN: Write minimum code to passtypescript
const add = (a, b) => a + b; // Test passes -
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红-绿-重构循环:
-
红(RED):先编写失败的测试typescript
test('adds numbers', () => { expect(add(1, 2)).toBe(3); // 失败 - add() 不存在 }); -
绿(GREEN):编写刚好能通过测试的代码typescript
const add = (a, b) => a + b; // 测试通过 -
重构(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
测试思维
- Tests are documentation - A failing test is a specification that hasn't been implemented
- Test behavior, not implementation - Tests should survive refactoring
- Fast feedback loops - Unit tests run in milliseconds, not seconds
- Isolation by default - Each test should be independent
- Arrange-Act-Assert - Clear structure in every test </core_principles>
<tdd_workflow>
- 测试即文档 - 失败的测试就是尚未实现的需求规格
- 测试行为而非实现 - 测试应能在代码重构后依然有效
- 快速反馈循环 - 单元测试应在毫秒级运行,而非秒级
- 默认隔离性 - 每个测试应相互独立
- 准备-执行-断言(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
规则
- Write a failing test first - Never write production code without a failing test
- Write only enough test to fail - Compilation failures count as failures
- Write only enough code to pass - No more, no less
- Refactor only when green - Never refactor with failing tests
- 先编写失败的测试 - 永远不要在没有失败测试的情况下编写生产代码
- 只编写刚好会失败的测试 - 编译错误也属于失败
- 只编写刚好能通过测试的代码 - 不多不少
- 仅在测试通过时重构 - 永远不要在测试失败时进行重构
Common TDD Mistakes
常见TDD错误
| Mistake | Why It's Wrong | Instead |
|---|---|---|
| Writing tests after code | Tests become confirmation bias | Red-Green-Refactor |
| Testing private methods | Tests implementation, not behavior | Test public interface |
| Big leaps in test complexity | Hard to debug failures | Baby steps |
| Skipping refactor step | Technical debt accumulates | Always 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
分布指南
| Type | Percentage | Speed | Scope |
|---|---|---|---|
| Unit | 70-80% | <10ms each | Single function/component |
| Integration | 15-25% | <1s each | Multiple components, DB |
| E2E | 5-10% | <30s each | Full 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的适用与不适用场景
| Mock | Don't Mock |
|---|---|
| External APIs | Your own pure functions |
| File system (in unit tests) | Data transformations |
| Network requests | Business logic |
| Time/randomness | In-memory data structures |
| Expensive computations | Simple 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.tssrc/
├── components/
│ ├── Button.tsx
│ └── Button.test.tsx // 与源代码同目录的测试文件
├── utils/
│ ├── format.ts
│ └── format.test.ts
└── __tests__/ // 或者单独的测试文件夹
└── integration/
└── api.test.tsTest 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
跳过以下内容
- Framework code - React's useState, Express routing
- Third-party libraries - They have their own tests
- Trivial getters/setters - No logic = no test needed
- Implementation details - Private methods, internal state
- One-line functions - Unless they have complex logic
- 框架代码 - React的useState、Express路由
- 第三方库 - 它们已有自己的测试
- 简单的getter/setter - 无逻辑则无需测试
- 实现细节 - 私有方法、内部状态
- 单行函数 - 除非包含复杂逻辑
Focus On
聚焦测试以下内容
- Business logic - Where bugs hide
- Edge cases - Nulls, empty arrays, boundaries
- Error paths - What happens when things fail
- User-facing behavior - What users actually do
- Regressions - Bugs that came back once
- 业务逻辑 - 漏洞常出现在此处
- 边界情况 - null值、空数组、临界值
- 错误路径 - 出现故障时的处理逻辑
- 用户可见行为 - 用户实际会操作的内容
- 回归问题 - 曾经出现过并修复的漏洞
Coverage Targets
覆盖率目标
| Metric | Target | Notes |
|---|---|---|
| Line coverage | 70-80% | Higher isn't always better |
| Branch coverage | 70-80% | More important than lines |
| Critical paths | 100% | 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:
| Topic | Reference File | When to Load |
|---|---|---|
| Unit testing patterns | | Writing unit tests, mocking |
| Integration testing | | API tests, database tests |
| Test organization | | Structuring test suites |
| Coverage strategies | | 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>
如需了解详细模式,请加载对应的参考文档:
| 主题 | 参考文件 | 加载时机 |
|---|---|---|
| 单元测试模式 | | 编写单元测试、Mock时 |
| 集成测试 | | API测试、数据库测试时 |
| 测试组织 | | 构建测试套件结构时 |
| 覆盖率策略 | | 设置覆盖率目标时 |
加载方式: 请求特定主题,或根据上下文判断是否需要加载。
</references>
<framework_patterns>
Quick Reference by Framework
各框架快速参考
pytest (Python)
pytest(Python)
python
undefinedpython
undefinedFixtures
夹具(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
undefinedvitest/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 原则
- Query by role, not test ID
- Test what users see, not implementation
- Prefer over
userEventfireEvent - Avoid testing internal state </framework_patterns>
- 按角色查询元素,而非测试ID
- 测试用户可见的内容,而非实现细节
- 优先使用而非
userEventfireEvent - 避免测试内部状态 </framework_patterns>
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
在标记代码完成前,请确认:
- 单元测试覆盖了主流程
- 单元测试覆盖了错误场景
- 边界情况已测试(null、空值、临界值)
- API接口已有集成测试
- 无不稳定测试(运行3次验证)
- 测试相互独立(可按任意顺序运行)
- 测试名称描述了行为
- 无硬编码超时(使用waitFor)
- Mock在测试间已重置
- 覆盖率符合项目标准