testing-unit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unit 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
rules/
loaded on-demand, plus reference material, checklists, and scaffolding scripts.
专注于编写独立、快速、可维护单元测试的模式。涵盖测试结构(AAA)、参数化、Fixture管理、HTTP Mock(MSW/VCR)以及基于工厂类的测试数据生成。
每个分类在
rules/
目录下都有独立的规则文件,可按需加载,同时包含参考资料、检查清单和脚手架脚本。

Core Principles (ALWAYS apply)

核心原则(必须遵守)

  1. AAA structure: Every test MUST follow Arrange-Act-Assert. Use
    // Arrange
    ,
    // Act
    ,
    // Assert
    comments for clarity.
  2. Parametrize, don't duplicate: Use
    test.each
    (TypeScript) or
    @pytest.mark.parametrize
    (Python) when testing multiple inputs. Never copy-paste the same test body with different values.
  3. Fixture scoping matters: Use
    scope="function"
    (default) for mutable data. Use
    scope="module"
    or
    scope="session"
    ONLY for expensive read-only resources (DB engines, ML models). Mutable data with shared scope causes flaky tests.
  4. Speed target: Each unit test should run under 100ms. If it's slower, you're likely hitting I/O — mock it.
  5. Mock at the network level: Use MSW (TypeScript) or VCR.py (Python) to intercept HTTP at the network layer. Never mock
    fetch
    /
    axios
    /
    requests
    directly.
  1. AAA结构:每个测试必须遵循Arrange-Act-Assert(准备-执行-断言)流程。使用
    // Arrange
    // Act
    // Assert
    注释提升可读性。
  2. 参数化而非重复:测试多组输入时,使用TypeScript的
    test.each
    或Python的
    @pytest.mark.parametrize
    。绝不要通过复制粘贴测试体来适配不同值。
  3. Fixture作用域至关重要:可变数据使用默认的
    scope="function"
    作用域。仅当处理昂贵的只读资源(如数据库引擎、机器学习模型)时,才使用
    scope="module"
    scope="session"
    作用域。共享作用域的可变数据会导致测试不稳定。
  4. 速度目标:每个单元测试的运行时间应控制在100毫秒以内。如果测试速度过慢,说明可能存在I/O操作——请对其进行Mock。
  5. 网络层Mock:使用TypeScript的MSW或Python的VCR.py在网络层拦截HTTP请求。绝不要直接Mock
    fetch
    /
    axios
    /
    requests

Quick Reference

快速参考

CategoryRulesImpactWhen to Use
Unit Test Structure3CRITICALWriting any unit test
HTTP Mocking2HIGHMocking API calls in frontend/backend tests
Test Data Management3MEDIUMSetting up test data, factories, fixtures
Total: 8 rules across 3 categories, 4 references, 3 checklists, 1 example set, 3 scripts
分类规则数量影响级别适用场景
单元测试结构3关键编写任何单元测试时
HTTP Mock2在前端/后端测试中Mock API调用时
测试数据管理3配置测试数据、工厂类、Fixture时
总计:3个分类下的8条规则、4份参考资料、3份检查清单、1组示例、3个脚本

Unit Test Structure

单元测试结构

Core patterns for structuring isolated unit tests with clear phases and efficient execution.
RuleFileKey Pattern
AAA Pattern
rules/unit-aaa-pattern.md
Arrange-Act-Assert with isolation
Fixture Scoping
rules/unit-fixture-scoping.md
function/module/session scope selection
Parametrized Tests
rules/unit-parametrized.md
test.each / @pytest.mark.parametrize
Reference:
references/aaa-pattern.md
— detailed AAA implementation with checklist
用于构建独立单元测试的核心模式,具备清晰的阶段划分和高效的执行效率。
规则文件核心模式
AAA模式
rules/unit-aaa-pattern.md
遵循隔离原则的Arrange-Act-Assert
Fixture作用域
rules/unit-fixture-scoping.md
function/module/session作用域选择
参数化测试
rules/unit-parametrized.md
test.each / @pytest.mark.parametrize
参考资料:
references/aaa-pattern.md
—— 包含检查清单的AAA模式详细实现指南

HTTP Mocking

HTTP Mock

Network-level request interception for deterministic tests without hitting real APIs.
RuleFileKey Pattern
MSW 2.x
rules/mocking-msw.md
Network-level mocking for frontend (TypeScript)
VCR.py
rules/mocking-vcr.md
Record/replay HTTP cassettes (Python)
References:
  • references/msw-2x-api.md
    — full MSW 2.x API (handlers, GraphQL, WebSocket, passthrough)
  • references/stateful-testing.md
    — Hypothesis RuleBasedStateMachine for stateful tests
Checklists:
  • checklists/msw-setup-checklist.md
    — MSW installation, handler setup, test writing
  • checklists/vcr-checklist.md
    — VCR configuration, sensitive data filtering, CI setup
Examples:
examples/handler-patterns.md
— CRUD, error simulation, auth flow, file upload handlers
基于网络层的请求拦截,无需调用真实API即可实现确定性测试。
规则文件核心模式
MSW 2.x
rules/mocking-msw.md
面向前端(TypeScript)的网络层Mock
VCR.py
rules/mocking-vcr.md
记录/重放HTTP cassette(Python)
参考资料:
  • references/msw-2x-api.md
    —— 完整的MSW 2.x API(处理器、GraphQL、WebSocket、透传)
  • references/stateful-testing.md
    —— 用于状态测试的Hypothesis RuleBasedStateMachine
检查清单:
  • checklists/msw-setup-checklist.md
    —— MSW安装、处理器配置、测试编写
  • checklists/vcr-checklist.md
    —— VCR配置、敏感数据过滤、CI环境设置
示例:
examples/handler-patterns.md
—— CRUD、错误模拟、认证流程、文件上传处理器

Test Data Management

测试数据管理

Factories, fixtures, and seeding patterns for isolated, realistic test data.
RuleFileKey Pattern
Data Factories
rules/data-factories.md
FactoryBoy / @faker-js builders
Data Fixtures
rules/data-fixtures.md
JSON fixtures with composition
Seeding & Cleanup
rules/data-seeding-cleanup.md
Automated DB seeding and teardown
Reference:
references/factory-patterns.md
— advanced factory patterns (Sequence, SubFactory, Traits)
Checklist:
checklists/test-data-checklist.md
— data generation, cleanup, isolation verification
用于生成独立、真实测试数据的工厂类、Fixture和数据填充模式。
规则文件核心模式
数据工厂类
rules/data-factories.md
FactoryBoy / @faker-js构建器
数据Fixture
rules/data-fixtures.md
支持组合的JSON Fixture
数据填充与清理
rules/data-seeding-cleanup.md
自动化数据库填充与销毁
参考资料:
references/factory-patterns.md
—— 高级工厂类模式(Sequence、SubFactory、Traits)
检查清单:
checklists/test-data-checklist.md
—— 数据生成、清理、隔离性验证

Quick 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_edit
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_edit

Vitest 4.1 Features

Vitest 4.1 新特性

aroundEach / aroundAll (preferred for DB transactions)

aroundEach / aroundAll(推荐用于数据库事务)

Wraps each test in setup/teardown — cleaner than separate
beforeEach
/
afterEach
for transactions:
typescript
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
wraps entire suites the same way.
将每个测试包裹在准备/销毁流程中——相比单独使用
beforeEach
/
afterEach
,这种方式处理事务更简洁:
typescript
test.aroundEach(async (runTest, { db }) => {
  await db.transaction(runTest)  // 测试结束时自动回滚
})

test('插入用户', async ({ db }) => {
  await db.insert({ name: 'Alice' })
  // 事务自动回滚——无需手动清理
})
aroundAll
以相同方式包裹整个测试套件。

mockThrow / mockThrowOnce

mockThrow / mockThrowOnce

Replaces the verbose
mockImplementation(() => { throw err })
pattern:
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

关键决策

DecisionRecommendation
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 dataFactories (FactoryBoy/faker-js) over hardcoded fixtures
Fixture scope
scope="function"
for mutable (default).
module
/
session
ONLY for expensive immutable resources
Execution timeUnder 100ms per unit test — if slower, mock external calls
Coverage target90%+ business logic, 100% critical paths
决策推荐方案
TypeScript测试框架Vitest 4.1+(现代化、快速、支持aroundEach、测试标签)或Jest(成熟生态)
Python测试框架搭配插件的pytest(支持参数化、asyncio、覆盖率统计)
TypeScript HTTP MockMSW 2.x(网络层Mock),绝不要直接Mock fetch/axios
Python HTTP MockVCR.py(搭配cassette),过滤敏感数据
测试数据优先使用工厂类(FactoryBoy/faker-js)而非硬编码Fixture
Fixture作用域可变数据使用
scope="function"
(默认)。仅当处理昂贵的不可变资源时,才使用
module
/
session
执行时间每个单元测试控制在100毫秒以内——如果速度过慢,Mock外部调用
覆盖率目标业务逻辑覆盖率90%+,关键路径覆盖率100%

Common Mistakes

常见误区

  1. Testing implementation details instead of public behavior (brittle tests)
  2. Mocking fetch/axios directly instead of using MSW at network level (incomplete coverage)
  3. Shared mutable state between tests via module-scoped fixtures (flaky tests)
  4. Hard-coded test data with duplicate IDs (test conflicts in parallel runs)
  5. No cleanup after database seeding (state leaks between tests)
  6. Over-mocking — testing your mocks instead of your code (false confidence)
  7. Verbose throw mocking
    mockImplementation(() => { throw err })
    instead of
    mockThrow(err)
    (Vitest 4.1+)
  1. 测试实现细节而非公开行为(导致测试脆弱)
  2. 直接Mock fetch/axios而非使用MSW进行网络层Mock(覆盖不完整)
  3. 通过模块作用域Fixture共享可变状态(导致测试不稳定)
  4. 使用硬编码的测试数据且存在重复ID(并行运行时测试冲突)
  5. 数据库填充后不清理(状态在测试间泄漏)
  6. 过度Mock——测试Mock而非业务代码(产生错误的信心)
  7. 冗长的抛出错误Mock——使用
    mockImplementation(() => { throw err })
    而非
    mockThrow(err)
    (Vitest 4.1+支持)

Scripts

脚本

ScriptFilePurpose
Create Test Case
scripts/create-test-case.md
Scaffold test file with auto-detected framework
Create Test Fixture
scripts/create-test-fixture.md
Scaffold pytest fixture with context detection
Create MSW Handler
scripts/create-msw-handler.md
Scaffold MSW handler for an API endpoint
脚本文件用途
创建测试用例
scripts/create-test-case.md
根据自动检测的框架生成测试文件模板
创建测试Fixture
scripts/create-test-fixture.md
根据上下文检测结果生成pytest Fixture模板
创建MSW处理器
scripts/create-msw-handler.md
为API端点生成MSW处理器模板