testing-unit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUnit Testing Patterns
单元测试模式
Focused patterns for writing isolated, fast, maintainable unit tests. Covers test structure (AAA), parametrization, fixture management, HTTP mocking (MSW/VCR), and test data generation with factories.
Each category has individual rule files in loaded on-demand, plus reference material, checklists, and scaffolding scripts.
rules/专注于编写独立、快速、可维护单元测试的模式。涵盖测试结构(AAA)、参数化、Fixture管理、HTTP Mock(MSW/VCR)以及基于工厂类的测试数据生成。
每个分类在目录下都有独立的规则文件,可按需加载,同时包含参考资料、检查清单和脚手架脚本。
rules/Core Principles (ALWAYS apply)
核心原则(必须遵守)
- AAA structure: Every test MUST follow Arrange-Act-Assert. Use ,
// Arrange,// Actcomments for clarity.// Assert - Parametrize, don't duplicate: Use (TypeScript) or
test.each(Python) when testing multiple inputs. Never copy-paste the same test body with different values.@pytest.mark.parametrize - Fixture scoping matters: Use (default) for mutable data. Use
scope="function"orscope="module"ONLY for expensive read-only resources (DB engines, ML models). Mutable data with shared scope causes flaky tests.scope="session" - Speed target: Each unit test should run under 100ms. If it's slower, you're likely hitting I/O — mock it.
- Mock at the network level: Use MSW (TypeScript) or VCR.py (Python) to intercept HTTP at the network layer. Never mock /
fetch/axiosdirectly.requests
- AAA结构:每个测试必须遵循Arrange-Act-Assert(准备-执行-断言)流程。使用、
// Arrange、// Act注释提升可读性。// Assert - 参数化而非重复:测试多组输入时,使用TypeScript的或Python的
test.each。绝不要通过复制粘贴测试体来适配不同值。@pytest.mark.parametrize - Fixture作用域至关重要:可变数据使用默认的作用域。仅当处理昂贵的只读资源(如数据库引擎、机器学习模型)时,才使用
scope="function"或scope="module"作用域。共享作用域的可变数据会导致测试不稳定。scope="session" - 速度目标:每个单元测试的运行时间应控制在100毫秒以内。如果测试速度过慢,说明可能存在I/O操作——请对其进行Mock。
- 网络层Mock:使用TypeScript的MSW或Python的VCR.py在网络层拦截HTTP请求。绝不要直接Mock /
fetch/axios。requests
Quick Reference
快速参考
| Category | Rules | Impact | When to Use |
|---|---|---|---|
| Unit Test Structure | 3 | CRITICAL | Writing any unit test |
| HTTP Mocking | 2 | HIGH | Mocking API calls in frontend/backend tests |
| Test Data Management | 3 | MEDIUM | Setting up test data, factories, fixtures |
Total: 8 rules across 3 categories, 4 references, 3 checklists, 1 example set, 3 scripts
Unit Test Structure
单元测试结构
Core patterns for structuring isolated unit tests with clear phases and efficient execution.
| Rule | File | Key Pattern |
|---|---|---|
| AAA Pattern | | Arrange-Act-Assert with isolation |
| Fixture Scoping | | function/module/session scope selection |
| Parametrized Tests | | test.each / @pytest.mark.parametrize |
Reference: — detailed AAA implementation with checklist
references/aaa-pattern.md用于构建独立单元测试的核心模式,具备清晰的阶段划分和高效的执行效率。
| 规则 | 文件 | 核心模式 |
|---|---|---|
| AAA模式 | | 遵循隔离原则的Arrange-Act-Assert |
| Fixture作用域 | | function/module/session作用域选择 |
| 参数化测试 | | test.each / @pytest.mark.parametrize |
参考资料: —— 包含检查清单的AAA模式详细实现指南
references/aaa-pattern.mdHTTP Mocking
HTTP Mock
Network-level request interception for deterministic tests without hitting real APIs.
| Rule | File | Key Pattern |
|---|---|---|
| MSW 2.x | | Network-level mocking for frontend (TypeScript) |
| VCR.py | | Record/replay HTTP cassettes (Python) |
References:
- — full MSW 2.x API (handlers, GraphQL, WebSocket, passthrough)
references/msw-2x-api.md - — Hypothesis RuleBasedStateMachine for stateful tests
references/stateful-testing.md
Checklists:
- — MSW installation, handler setup, test writing
checklists/msw-setup-checklist.md - — VCR configuration, sensitive data filtering, CI setup
checklists/vcr-checklist.md
Examples: — CRUD, error simulation, auth flow, file upload handlers
examples/handler-patterns.md基于网络层的请求拦截,无需调用真实API即可实现确定性测试。
| 规则 | 文件 | 核心模式 |
|---|---|---|
| MSW 2.x | | 面向前端(TypeScript)的网络层Mock |
| VCR.py | | 记录/重放HTTP cassette(Python) |
参考资料:
- —— 完整的MSW 2.x API(处理器、GraphQL、WebSocket、透传)
references/msw-2x-api.md - —— 用于状态测试的Hypothesis RuleBasedStateMachine
references/stateful-testing.md
检查清单:
- —— MSW安装、处理器配置、测试编写
checklists/msw-setup-checklist.md - —— VCR配置、敏感数据过滤、CI环境设置
checklists/vcr-checklist.md
示例: —— CRUD、错误模拟、认证流程、文件上传处理器
examples/handler-patterns.mdTest Data Management
测试数据管理
Factories, fixtures, and seeding patterns for isolated, realistic test data.
| Rule | File | Key Pattern |
|---|---|---|
| Data Factories | | FactoryBoy / @faker-js builders |
| Data Fixtures | | JSON fixtures with composition |
| Seeding & Cleanup | | Automated DB seeding and teardown |
Reference: — advanced factory patterns (Sequence, SubFactory, Traits)
references/factory-patterns.mdChecklist: — data generation, cleanup, isolation verification
checklists/test-data-checklist.md用于生成独立、真实测试数据的工厂类、Fixture和数据填充模式。
| 规则 | 文件 | 核心模式 |
|---|---|---|
| 数据工厂类 | | FactoryBoy / @faker-js构建器 |
| 数据Fixture | | 支持组合的JSON Fixture |
| 数据填充与清理 | | 自动化数据库填充与销毁 |
参考资料: —— 高级工厂类模式(Sequence、SubFactory、Traits)
references/factory-patterns.md检查清单: —— 数据生成、清理、隔离性验证
checklists/test-data-checklist.mdQuick Start
快速开始
TypeScript (Vitest + MSW)
TypeScript(Vitest + MSW)
typescript
import { describe, test, expect, beforeAll, afterEach, afterAll } from 'vitest';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { calculateDiscount } from './pricing';
// 1. Pure unit test with AAA pattern
describe('calculateDiscount', () => {
test.each([
[100, 0],
[150, 15],
[200, 20],
])('for order $%i returns $%i discount', (total, expected) => {
// Arrange
const order = { total };
// Act
const discount = calculateDiscount(order);
// Assert
expect(discount).toBe(expected);
});
});
// 2. MSW mocked API test
const server = setupServer(
http.get('/api/users/:id', ({ params }) => {
return HttpResponse.json({ id: params.id, name: 'Test User' });
})
);
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('fetches user from API', async () => {
// Arrange — MSW handler set up above
// Act
const response = await fetch('/api/users/123');
const data = await response.json();
// Assert
expect(data.name).toBe('Test User');
});typescript
import { describe, test, expect, beforeAll, afterEach, afterAll } from 'vitest';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { calculateDiscount } from './pricing';
// 1. 基于AAA模式的纯单元测试
describe('calculateDiscount', () => {
test.each([
[100, 0],
[150, 15],
[200, 20],
])('订单金额为$%i时返回$%i折扣', (total, expected) => {
// Arrange
const order = { total };
// Act
const discount = calculateDiscount(order);
// Assert
expect(discount).toBe(expected);
});
});
// 2. 基于MSW的Mock API测试
const server = setupServer(
http.get('/api/users/:id', ({ params }) => {
return HttpResponse.json({ id: params.id, name: 'Test User' });
})
);
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('从API获取用户信息', async () => {
// Arrange — MSW处理器已在上方配置
// Act
const response = await fetch('/api/users/123');
const data = await response.json();
// Assert
expect(data.name).toBe('Test User');
});Python (pytest + FactoryBoy)
Python(pytest + FactoryBoy)
python
import pytest
from factory import Factory, Faker, SubFactory
class UserFactory(Factory):
class Meta:
model = dict
email = Faker('email')
name = Faker('name')
class TestUserService:
@pytest.mark.parametrize("role,can_edit", [
("admin", True),
("viewer", False),
])
def test_edit_permission(self, role, can_edit):
# Arrange
user = UserFactory(role=role)
# Act
result = user_can_edit(user)
# Assert
assert result == can_editpython
import pytest
from factory import Factory, Faker, SubFactory
class UserFactory(Factory):
class Meta:
model = dict
email = Faker('email')
name = Faker('name')
class TestUserService:
@pytest.mark.parametrize("role,can_edit", [
("admin", True),
("viewer", False),
])
def test_edit_permission(self, role, can_edit):
# Arrange
user = UserFactory(role=role)
# Act
result = user_can_edit(user)
# Assert
assert result == can_editVitest 4.1 Features
Vitest 4.1 新特性
aroundEach / aroundAll (preferred for DB transactions)
aroundEach / aroundAll(推荐用于数据库事务)
Wraps each test in setup/teardown — cleaner than separate / for transactions:
beforeEachafterEachtypescript
test.aroundEach(async (runTest, { db }) => {
await db.transaction(runTest) // auto-rollback on test end
})
test('insert user', async ({ db }) => {
await db.insert({ name: 'Alice' })
// transaction auto-rolls back — no cleanup needed
})aroundAll将每个测试包裹在准备/销毁流程中——相比单独使用/,这种方式处理事务更简洁:
beforeEachafterEachtypescript
test.aroundEach(async (runTest, { db }) => {
await db.transaction(runTest) // 测试结束时自动回滚
})
test('插入用户', async ({ db }) => {
await db.insert({ name: 'Alice' })
// 事务自动回滚——无需手动清理
})aroundAllmockThrow / mockThrowOnce
mockThrow / mockThrowOnce
Replaces the verbose pattern:
mockImplementation(() => { throw err })typescript
const fn = vi.fn()
fn.mockThrow(new Error('connection lost')) // always throws
fn.mockThrowOnce(new Error('timeout')) // throws once, then normal替代冗长的模式:
mockImplementation(() => { throw err })typescript
const fn = vi.fn()
fn.mockThrow(new Error('connection lost')) // 始终抛出错误
fn.mockThrowOnce(new Error('timeout')) // 仅抛出一次错误,后续恢复正常vi.defineHelper (clean stack traces)
vi.defineHelper(清晰的堆栈跟踪)
Custom assertion helpers that point errors to the call site, not the helper internals:
typescript
const assertPair = vi.defineHelper((a, b) => {
expect(a).toEqual(b) // error points to where assertPair() was CALLED
})自定义断言助手,错误信息会指向调用位置而非助手内部:
typescript
const assertPair = vi.defineHelper((a, b) => {
expect(a).toEqual(b) // 错误指向assertPair()的调用位置
})Test Tags
测试标签
Filter tests by tags in CLI — useful for CI fast paths:
typescript
// vitest.config.ts
test: {
tags: {
unit: { timeout: 5000 },
flaky: { retry: 3 },
}
}bash
vitest --tags-filter="unit and !flaky"
vitest --tags-filter="(unit or integration) and !slow"通过CLI按标签筛选测试——适用于CI快速执行路径:
typescript
// vitest.config.ts
test: {
tags: {
unit: { timeout: 5000 },
flaky: { retry: 3 },
}
}bash
vitest --tags-filter="unit and !flaky"
vitest --tags-filter="(unit or integration) and !slow"Agent Reporter
Agent 报告器
Minimal output (failures only) — use in AI agent / CI contexts:
bash
AI_AGENT=copilot vitest # auto-detect agent mode极简输出(仅显示失败用例)——适用于AI Agent / CI环境:
bash
AI_AGENT=copilot vitest # 自动检测Agent模式Key Decisions
关键决策
| Decision | Recommendation |
|---|---|
| Test framework (TS) | Vitest 4.1+ (modern, fast, aroundEach, test tags) or Jest (mature ecosystem) |
| Test framework (Python) | pytest with plugins (parametrize, asyncio, cov) |
| HTTP mocking (TS) | MSW 2.x at network level, never mock fetch/axios directly |
| HTTP mocking (Python) | VCR.py with cassettes, filter sensitive data |
| Test data | Factories (FactoryBoy/faker-js) over hardcoded fixtures |
| Fixture scope | |
| Execution time | Under 100ms per unit test — if slower, mock external calls |
| Coverage target | 90%+ business logic, 100% critical paths |
| 决策 | 推荐方案 |
|---|---|
| TypeScript测试框架 | Vitest 4.1+(现代化、快速、支持aroundEach、测试标签)或Jest(成熟生态) |
| Python测试框架 | 搭配插件的pytest(支持参数化、asyncio、覆盖率统计) |
| TypeScript HTTP Mock | MSW 2.x(网络层Mock),绝不要直接Mock fetch/axios |
| Python HTTP Mock | VCR.py(搭配cassette),过滤敏感数据 |
| 测试数据 | 优先使用工厂类(FactoryBoy/faker-js)而非硬编码Fixture |
| Fixture作用域 | 可变数据使用 |
| 执行时间 | 每个单元测试控制在100毫秒以内——如果速度过慢,Mock外部调用 |
| 覆盖率目标 | 业务逻辑覆盖率90%+,关键路径覆盖率100% |
Common Mistakes
常见误区
- Testing implementation details instead of public behavior (brittle tests)
- Mocking fetch/axios directly instead of using MSW at network level (incomplete coverage)
- Shared mutable state between tests via module-scoped fixtures (flaky tests)
- Hard-coded test data with duplicate IDs (test conflicts in parallel runs)
- No cleanup after database seeding (state leaks between tests)
- Over-mocking — testing your mocks instead of your code (false confidence)
- Verbose throw mocking — instead of
mockImplementation(() => { throw err })(Vitest 4.1+)mockThrow(err)
- 测试实现细节而非公开行为(导致测试脆弱)
- 直接Mock fetch/axios而非使用MSW进行网络层Mock(覆盖不完整)
- 通过模块作用域Fixture共享可变状态(导致测试不稳定)
- 使用硬编码的测试数据且存在重复ID(并行运行时测试冲突)
- 数据库填充后不清理(状态在测试间泄漏)
- 过度Mock——测试Mock而非业务代码(产生错误的信心)
- 冗长的抛出错误Mock——使用而非
mockImplementation(() => { throw err })(Vitest 4.1+支持)mockThrow(err)
Scripts
脚本
| Script | File | Purpose |
|---|---|---|
| Create Test Case | | Scaffold test file with auto-detected framework |
| Create Test Fixture | | Scaffold pytest fixture with context detection |
| Create MSW Handler | | Scaffold MSW handler for an API endpoint |
| 脚本 | 文件 | 用途 |
|---|---|---|
| 创建测试用例 | | 根据自动检测的框架生成测试文件模板 |
| 创建测试Fixture | | 根据上下文检测结果生成pytest Fixture模板 |
| 创建MSW处理器 | | 为API端点生成MSW处理器模板 |