← tdd · 技能图谱

When to Mock

何时使用模拟

Mock at system boundaries only:

仅在系统边界处使用模拟:

  • External APIs (payment, email, etc.)
  • Databases (sometimes - prefer test DB)
  • Time/randomness
  • File system (sometimes)
  • 外部 API(支付、邮件等)
  • 数据库(有时——优先使用测试数据库)
  • 时间/随机性
  • 文件系统(有时)

Don't mock:

不要模拟:

  • Your own classes/modules
  • Internal collaborators
  • Anything you control
  • 你自己的类/模块
  • 内部协作者
  • 任何你可以控制的东西

Designing for Mockability

为可模拟性而设计

At system boundaries, design interfaces that are easy to mock:

在系统边界处,设计易于模拟的接口:

1. Use dependency injection

1. 使用依赖注入

Pass external dependencies in rather than creating them internally:

将外部依赖传入而非在内部创建:

// Easy to mock
function processPayment(order, paymentClient) {
  return paymentClient.charge(order.total);
}

// Hard to mock
function processPayment(order) {
  const client = new StripeClient(process.env.STRIPE_KEY);
  return client.charge(order.total);
}
// 易于模拟
function processPayment(order, paymentClient) {
  return paymentClient.charge(order.total);
}

// 难以模拟
function processPayment(order) {
  const client = new StripeClient(process.env.STRIPE_KEY);
  return client.charge(order.total);
}

2. Prefer SDK-style interfaces over generic fetchers

2. 优先使用 SDK 风格的接口而非通用请求器

Create specific functions for each external operation instead of one generic function with conditional logic:

为每个外部操作创建特定的函数,而不是一个带有条件逻辑的通用函数:

// GOOD: Each function is independently mockable
const api = {
  getUser: (id) => fetch(`/users/${id}`),
  getOrders: (userId) => fetch(`/users/${userId}/orders`),
  createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
};

// BAD: Mocking requires conditional logic inside the mock
const api = {
  fetch: (endpoint, options) => fetch(endpoint, options),
};
// 好:每个函数都可独立模拟
const api = {
  getUser: (id) => fetch(`/users/${id}`),
  getOrders: (userId) => fetch(`/users/${userId}/orders`),
  createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
};

// 坏:模拟需要在内部使用条件逻辑
const api = {
  fetch: (endpoint, options) => fetch(endpoint, options),
};

The SDK approach means:

SDK 方法意味着:

  • Each mock returns one specific shape
  • No conditional logic in test setup
  • Easier to see which endpoints a test exercises
  • Type safety per endpoint
  • 每个模拟返回一个特定形状
  • 测试设置中没有条件逻辑
  • 更容易看到测试用到了哪些端点
  • 每个端点的类型安全