为复杂外部依赖写精简 mock,核心是只模拟实际用到的接口行为,不复制真实逻辑,不覆盖未调用路径;重点在“够不够用”,确保测试快速、稳定、隔离地跑通关键路径。

为复杂外部依赖写精简 Mock,核心是只模拟你实际用到的接口行为,不复制真实实现逻辑,也不覆盖未调用的路径。重点不在“像不像”,而在“够不够用”——让测试能快速、稳定、隔离地跑通关键路径。
只暴露测试需要的最小接口集合
真实 SDK 或库常有几十个方法,但单个测试通常只用其中 2~3 个。Mock 时别照搬整个 API 表面结构,而是按用例反推:
- 看测试里 实际调用了哪些方法(比如
auth.login()和api.fetchUser()) - 提取这些方法的入参形状、返回值类型、可能抛出的错误场景
- 用 plain object 或 class 实现仅含这几个方法的轻量对象,其余一概不定义
例如 mock 一个第三方支付 SDK:
const mockPaymentSDK = {
init: jest.fn().mockResolvedValue({ sessionId: 'sess_123' }),
createOrder: jest.fn().mockResolvedValue({ orderId: 'ord_456', status: 'created' }),
// 不实现 refund / queryStatus 等未在当前测试中使用的函数
};
用状态机代替硬编码返回值
当测试涉及多次调用或条件分支(如“第一次失败,第二次成功”),避免写一堆 mockReturnValueOnce 堆砌。改用简单状态机控制行为:
- 用闭包变量记录调用次数或当前状态(如
attempts = 0) - 在 mock 函数体内根据状态返回不同结果或抛错
- 测试结束前重置状态,保证可复用
示例:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
let loginAttempt = 0;
const mockAuth = {
login: () => {
loginAttempt++;
if (loginAttempt === 1) throw new Error('Network timeout');
return { token: 'abc123' };
},
reset: () => { loginAttempt = 0; }
};
把网络/副作用剥离到可注入层
真正难 Mock 的往往是底层 fetch / axios / WebSocket 等。解决办法不是 mock 底层,而是让业务代码依赖抽象接口,再把真实实现作为参数注入:
- 将请求逻辑封装成独立函数(如
fetchWithRetry),测试时传入 mock 版本 - 使用依赖注入模式:构造函数或函数参数接收 client 实例,而非直接 import
- 这样 Mock 只需替换一个纯函数或轻量对象,无需动全局 fetch 或打补丁
例如:
class UserService {
constructor(httpClient) {
this.http = httpClient; // 而非直接用 axios
}
async getCurrentUser() {
return this.http.get('/user');
}
}
// 测试中:
const mockHttp = { get: jest.fn().mockResolvedValue({ id: 1 }) };
const service = new UserService(mockHttp);
用类型守卫减少运行时意外
Mock 返回值若与真实类型不一致(比如少一个字段、错一个类型),测试可能侥幸通过,但上线后崩溃。在 Mock 定义处加一层轻量校验:
- 用
as const或satisfies(TS 4.9+)锁定返回结构 - 对关键字段做存在性断言(如
if (!res.token) throw new Error('missing token')) - 在测试 setup 中统一检查 mock 对象是否满足最小 interface
这样既保持 Mock 简洁,又守住契约底线。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










