核心是通过接口抽象、依赖注入和多态mock实现可测性:先将外部服务提炼为职责明确的接口,再让业务代码依赖接口而非具体实现,接着用mock框架生成类型一致的模拟对象,最后验证交互行为而非仅返回值。

核心在于把依赖“提出来”,用接口定义行为,再用多态让测试时能无缝替换成模拟实现。不改业务逻辑,只调整构造方式和类型声明。
第一步:把外部服务抽象成接口
真实服务(比如支付网关、短信 SDK、RPC 客户端)往往带具体实现或强耦合,必须先剥离。关键不是重写功能,而是提取它对外承诺的契约:
- 所有方法声明为 public,参数和返回值明确
- 避免在接口里暴露实现细节(如 HTTP client、连接池、日志器)
- 接口名体现职责,例如
PaymentService、SmsSender,而不是AlipayClientImpl
这样后续才能用 mock 类、fake 类或真实类统一替换,编译期类型安全,运行期行为可控。
第二步:业务代码依赖接口,而非具体实现
原代码如果直接 new 一个第三方客户端,就无法 mock。要改为通过构造函数或 setter 注入:
- 将接口作为成员变量,用 private final 声明
- 构造函数接收该接口,不自行创建实例
- 避免静态方法调用或单例全局访问——这些会破坏可测性
例如,把 new WeChatPayClient() 换成 this.payService = payService;。这样单元测试里传入 mock 对象,业务逻辑完全感知不到差异。
第三步:用 Mock 框架生成多态替代品
接口定义好、依赖注入到位后,mock 就是水到渠成的事。不同语言有对应工具:
- Java + Mockito:用
@Mock声明,when(...).thenReturn(...)设定响应 - Go + GoMock:用
mockgen自动生成实现,支持期望校验(EXPECT().Method().Return(...)) - C++ + gMock:继承接口类,用
MOCK_METHOD声明虚函数模拟,注意基类析构函数必须virtual
重点是:所有 mock 对象都和原接口类型一致,靠多态机制在运行时完成替换,编译器不报错,测试逻辑清晰。
第四步:验证交互,不止看返回值
mock 不只是设返回值,更是为了验证“是否按预期调用了依赖”。这是多态 mock 的高阶价值:
- 检查方法是否被调用(
verify(xxx).method()) - 验证调用次数(
times(1))、参数(eq("order_123"))、顺序(配合InOrder) - 模拟异常路径:让 mock 方法抛出特定异常,确认业务能否正确捕获和降级
这种基于行为的验证,比只断言输出更贴近真实协作场景,也更容易暴露隐藏的耦合问题。











