测试模式
构建可靠测试套件的原则。
1. 测试金字塔
/\ E2E (Few)
/ \ Critical flows
/----\
/ \ Integration (Some)
/--------\ API, DB queries
/ \
/------------\ Unit (Many)
Functions, classes
2. AAA 模式
| 步骤 | 用途 |
|---|
| Arrange(准备) | 设置测试数据 |
| Act(执行) | 执行待测代码 |
| Assert(断言) | 验证结果 |
3. 测试类型选择
何时使用各类型
| 类型 | 最适用于 | 速度 |
|---|
| 单元测试 | 纯函数、逻辑 | 快(<100ms) |
| 集成测试 | API、DB、服务 | 中等 |
| 端到端测试 | 关键用户流程 | 慢 |
4. 单元测试原则
良好的单元测试
| 原则 | 含义 |
|---|
| 快速 | 每个 < 100ms |
| 隔离 | 不依赖外部内容 |
| 可重复 | 结果始终相同 |
| 自检 | 无需人工验证 |
| 及时 | 与代码一同编写 |
单元测试的内容
| 要测试 | 不要测试 |
|---|
| 业务逻辑 | 框架代码 |
| 边界情况 | 第三方库 |
| 错误处理 | 简单的 getter |
5. 集成测试原则
测试内容
| 范围 | 重点 |
|---|
| API 端点 | 请求/响应 |
| 数据库 | 查询、事务 |
| 外部服务 | 合约 |
设置/清理
| 阶段 | 操作 |
|---|
| 全部之前 | 连接资源 |
| 每项之前 | 重置状态 |
| 每项之后 | 清理 |
| 全部之后 | 断开连接 |
6. 模拟原则
何时使用模拟
| 模拟 | 不要模拟 |
|---|
| 外部 API | 待测代码 |
| 数据库(单元测试) | 简单依赖 |
| 时间/随机数 | 纯函数 |
| 网络 | 内存存储 |
模拟类型
| 类型 | 用途 |
|---|
| Stub | 返回固定值 |
| Spy | 跟踪调用 |
| Mock | 设置预期 |
| Fake | 简化实现 |
7. 测试组织
命名
| 模式 | 示例 |
|---|
| 描述应有行为 | “应在……时返回错误” |
| 描述条件 | “当用户未找到时……” |
| Given-when-then | “给定 X,当 Y 时,则 Z” |
分组
| 层级 | 用途 |
|---|
| describe | 对相关测试分组 |
| it/test | 单个用例 |
| beforeEach | 通用设置 |
8. 测试数据
策略
| 方法 | 用途 |
|---|
| 工厂 | 生成测试数据 |
| Fixtures | 预定义数据集 |
| Builder | 流畅地创建对象 |
原则
- 使用真实的数据
- 随机化非必要值(faker)
- 共享常用 fixtures
- 尽量精简数据
9. 最佳实践
| 实践 | 原因 |
|---|
| 每个测试只做一个断言 | 失败原因清晰 |
| 测试相互独立 | 不依赖执行顺序 |
| 保持测试快速 | 频繁运行 |
| 使用描述性名称 | 自我说明 |
| 做好清理 | 避免副作用 |
10. 反模式
| ❌ 不要 | ✅ 应该 |
|---|
| 测试实现细节 | 测试行为 |
| 重复测试代码 | 使用工厂 |
| 复杂的测试设置 | 简化或拆分 |
| 忽视不稳定的测试 | 修复根本原因 |
| 跳过清理 | 重置状态 |
请记住: 测试就是文档。如果有人无法通过测试理解代码的作用,就重写测试。