关于 testing-unit
testing-unit 提供编写隔离且快速的单元测试指南,面向使用 TypeScript 或 Python 的开发者,涵盖测试结构、HTTP 拦截、夹具和测试数据生成。
主要功能
- 01Arrange-Act-Assert 测试结构
- 02参数化测试以避免重复
- 03夹具作用域与隔离指南
- 04网络层 HTTP 拦截
- 05工厂与夹具数据管理
- 06测试脚手架脚本
适用场景
- 01组织隔离的单元测试
- 02不访问真实 API 测试 HTTP 相关代码
- 03创建可复用测试数据与夹具
- 04生成测试文件和 MSW 处理器
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yonatangross/ orchestkit --skill testing-unit安装后在 Claude Code 中输入/testing-unit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
test-cases.jsonchecklists/msw-setup-checklist.mdtest-data-checklist.mdvcr-checklist.mdexamples/handler-patterns.mdreferences/aaa-pattern.mdfactory-patterns.mdmsw-2x-api.mdstateful-testing.mdrules/_sections.mddata-factories.mddata-fixtures.mddata-seeding-cleanup.mdmocking-msw.mdmocking-vcr.mdunit-aaa-pattern.mdunit-fixture-scoping.mdunit-parametrized.mdscripts/create-msw-handler.mdcreate-test-case.mdcreate-test-fixture.md详细介绍
单元测试模式
专注于编写隔离、高速且易于维护的单元测试模式。涵盖测试结构(AAA)、参数化、fixture 管理、HTTP 模拟(MSW/VCR)以及使用工厂生成测试数据。
每个类别都有独立的规则文件,按需从 rules/ 加载;此外还提供参考资料、检查清单和脚手架脚本。
核心原则(始终遵循)
- AAA 结构:每个测试都必须遵循 Arrange-Act-Assert。使用
// Arrange、// Act、// Assert注释,提高清晰度。 - 使用参数化,不要重复编写:测试多个输入时,使用
test.each(TypeScript)或@pytest.mark.parametrize(Python)。切勿复制粘贴测试主体,只更改不同的值。 - Fixture 作用域很重要:对于可变数据,使用
scope="function"(默认值)。只有对于开销较大的只读资源(DB 引擎、ML 模型),才使用scope="module"或scope="session"。共享作用域中的可变数据会导致测试不稳定。 - 速度目标:每个单元测试都应在 100ms 内运行完毕。如果更慢,你很可能触发了 I/O 操作——请模拟它。
- 在网络层模拟:使用 MSW(TypeScript)或 VCR.py(Python)在网络层拦截 HTTP。切勿直接模拟
fetch/axios/requests。
快速参考
| 类别 | 规则 | 影响 | 适用场景 |
|---|---|---|---|
| 单元测试结构 | 3 | 严重 | 编写任何单元测试时 |
| HTTP 模拟 | 2 | 高 | 在前端/后端测试中模拟 API 调用时 |
| 测试数据管理 | 3 | 中 | 设置测试数据、工厂和 fixture 时 |
总计:3 个类别中的 8 条规则、4 份参考资料、3 份检查清单、1 组示例、3 个脚本
单元测试结构
用于构建隔离单元测试的核心模式,包含清晰的阶段并可高效执行。
| 规则 | 文件 | 关键模式 |
|---|---|---|
| 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 模拟
在网络层拦截请求,以运行确定性测试,而无需访问真实 API。
| 规则 | 文件 | 关键模式 |
|---|---|---|
| MSW 2.x | rules/mocking-msw.md | 面向前端的网络层模拟(TypeScript) |
| VCR.py | rules/mocking-vcr.md | 录制/回放 HTTP 磁带(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、错误模拟、身份验证流程、文件上传处理器
测试数据管理
用于生成隔离且真实的测试数据的工厂、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 — 数据生成、清理、隔离验证
快速开始
TypeScript(Vitest + MSW)
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');
});
Python(pytest + FactoryBoy)
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 特性
aroundEach / aroundAll(推荐用于数据库事务)
通过设置/拆卸包装每个测试——用于事务时,比单独使用 beforeEach/afterEach 更简洁:
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 以相同方式包装整个测试套件。
mockThrow / mockThrowOnce
替代冗长的 mockImplementation(() => { throw err }) 模式:
const fn = vi.fn()
fn.mockThrow(new Error('connection lost')) // always throws
fn.mockThrowOnce(new Error('timeout')) // throws once, then normal
vi.defineHelper(简洁的堆栈跟踪)
自定义断言辅助函数,可将错误指向调用位置,而不是辅助函数内部:
const assertPair = vi.defineHelper((a, b) => {
expect(a).toEqual(b) // error points to where assertPair() was CALLED
})
测试标签
在命令行中按标签筛选测试——适用于 CI 快速路径:
// vitest.config.ts
test: {
tags: {
unit: { timeout: 5000 },
flaky: { retry: 3 },
}
}
vitest --tags-filter="unit and !flaky"
vitest --tags-filter="(unit or integration) and !slow"
Agent 报告器
最简输出(仅显示失败项)——适用于 AI Agent / CI 场景:
AI_AGENT=copilot vitest # auto-detect agent mode
关键决策
| 决策 | 建议 |
|---|---|
| 测试框架(TS) | Vitest 4.1+(现代、快速,支持 aroundEach、测试标签)或 Jest(生态成熟) |
| 测试框架(Python) | 搭配插件使用 pytest(parametrize、asyncio、cov) |
| HTTP 模拟(TS) | 在网络层使用 MSW 2.x,切勿直接模拟 fetch/axios |
| HTTP 模拟(Python) | 使用 VCR.py 和录制文件,并过滤敏感数据 |
| 测试数据 | 优先使用工厂(FactoryBoy/faker-js),而非硬编码的测试固件 |
| 测试固件作用域 | 可变测试固件使用 scope="function"(默认值)。仅对开销大的不可变资源使用 module/session |
| 执行时间 | 每个单元测试不超过 100ms——如果更慢,请模拟外部调用 |
| 覆盖率目标 | 业务逻辑达到 90%+,关键路径达到 100% |
常见错误
- 测试实现细节而非公开行为(测试脆弱)
- 直接模拟 fetch/axios,而不是在网络层使用 MSW(覆盖不完整)
- 通过模块级测试固件在测试之间共享可变状态(测试不稳定)
- 使用包含重复 ID 的硬编码测试数据(并行运行时发生测试冲突)
- 数据库填充后未清理(测试之间状态泄漏)
- 过度模拟——测试模拟对象而不是代码(产生虚假的信心)
- 冗长的抛错模拟——使用
mockImplementation(() => { throw err })而非mockThrow(err)(Vitest 4.1+)
脚本
| 脚本 | 文件 | 用途 |
|---|---|---|
| 创建测试用例 | scripts/create-test-case.md | 根据自动检测到的框架搭建测试文件 |
| 创建测试固件 | scripts/create-test-fixture.md | 根据上下文检测搭建 pytest 测试固件 |
| 创建 MSW 处理器 | scripts/create-msw-handler.md | 为 API 端点搭建 MSW 处理器 |