62 lines
1.6 KiB
Markdown
62 lines
1.6 KiB
Markdown
# 好的测试与糟糕的测试
|
|
|
|
## 好的测试
|
|
|
|
**集成风格**:通过真实接口进行测试,而非模拟内部组件。
|
|
|
|
```typescript
|
|
// 好:测试可观察的行为
|
|
test("用户可以使用有效购物车完成结账", async () => {
|
|
const cart = createCart();
|
|
cart.add(product);
|
|
const result = await checkout(cart, paymentMethod);
|
|
expect(result.status).toBe("confirmed");
|
|
});
|
|
```
|
|
|
|
特征:
|
|
|
|
- 测试用户/调用者关心的行为
|
|
- 仅使用公开 API
|
|
- 能够经受内部重构
|
|
- 描述「做什么」,而非「怎么做」
|
|
- 每个测试只有一个逻辑断言
|
|
|
|
## 糟糕的测试
|
|
|
|
**实现细节测试**:与内部结构耦合。
|
|
|
|
```typescript
|
|
// 差:测试实现细节
|
|
test("结账时调用 paymentService.process", async () => {
|
|
const mockPayment = jest.mock(paymentService);
|
|
await checkout(cart, payment);
|
|
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
|
|
});
|
|
```
|
|
|
|
危险信号:
|
|
|
|
- 模拟内部协作对象
|
|
- 测试私有方法
|
|
- 对调用次数/顺序进行断言
|
|
- 重构(未改变行为)时测试破裂
|
|
- 测试名称描述「怎么做」而非「做什么」
|
|
- 通过外部手段而非接口进行验证
|
|
|
|
```typescript
|
|
// 差:绕过接口进行验证
|
|
test("createUser 保存到数据库", async () => {
|
|
await createUser({ name: "Alice" });
|
|
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
|
|
expect(row).toBeDefined();
|
|
});
|
|
|
|
// 好:通过接口进行验证
|
|
test("createUser 使用户可被检索到", async () => {
|
|
const user = await createUser({ name: "Alice" });
|
|
const retrieved = await getUser(user.id);
|
|
expect(retrieved.name).toBe("Alice");
|
|
});
|
|
```
|