JS Mock专家级实践核心是“mock谁、何时mock、如何验证、怎样收口”,需精准拦截业务依赖、分层注入HTTP客户端、语义化生成符合业务规则的数据、行为验证而非调用路径断言、全环境收敛管理。

JS 中做 Mock 模拟的专家级全链路测试解耦调优,关键不在“怎么 mock”,而在“mock 谁、何时 mock、如何验证、怎样收口”。它是一套贯穿开发、测试、联调、回归的工程实践,不是零散技巧堆砌。核心目标是:让每一次请求、每一层依赖、每一种异常路径,都可预测、可控制、可断言、可清理。
精准拦截:只 mock 你真正调用的那一层
避免全局污染和底层补丁,比如不 mock XMLHttpRequest.prototype.open 或 fetch 原生函数(除非调试需要)。应聚焦业务直接依赖:
- 若用 axios.get('/api/user'),就 mock axios.get,而非 axios 内部的 adapter
- 若封装了 apiClient.getUser(id),就 mock 这个方法本身,返回预设数据或抛出错误
- 使用 jest.mock('axios') 或 vi.mock('@/utils/request'),确保只影响被测模块,不波及其他测试
分层注入:把网络能力变成可替换的参数
硬编码 fetch 或 axios 实例会让测试僵化。改用依赖注入提升可测性与类型安全:
- 服务类构造时接收 httpClient:class UserService { constructor(httpClient) { this.http = httpClient; } }
- 测试中传入 fake client:new UserService({ get: vi.fn().mockResolvedValue(mockUser) })
- 配合 TypeScript 接口(如 interface HttpClient { get
(url: string): Promise ),替换时类型不报错,重构更安心; }
语义化模拟:数据不是随机,而是符合业务规则
Mock.js 等工具生成的假数据要带业务含义,不能全是 @cname + @natural 的无意义组合:
- 用户状态字段用枚举值模拟:'status|1': ['active', 'disabled', 'pending']
- 时间字段按逻辑生成:'createdAt|+1': '@datetime("yyyy-MM-dd HH:mm")',保证顺序合理
- 分页响应结构对齐真实接口:'list|5': [...], 'total|100-1000': 1, 'page|1-5': 1, 'pageSize|10': 1
- 错误场景单独建模:401 返回空 body + statusText,404 返回 { code: 404, message: 'Not found' }
行为验证:断言结果,而非调用路径
测试重点永远是“功能是否正确”,不是“有没有调 fetch”:
- 主断言检查返回值结构、字段类型、业务逻辑分支(如:按钮是否禁用、列表是否渲染、错误提示是否显示)
- 用 vi.spyOn 或 jest.spyOn 辅助确认调用次数和参数,仅用于流程校验,不作为通过依据
- 对异步边界全覆盖:成功、超时(vi.useFakeTimers())、网络中断、JSON 解析失败、空数组、单条数据等
- 每个测试用例结尾加 vi.clearAllMocks() 或 jest.clearAllMocks(),防止跨测试污染
环境收敛:开发、测试、联调一套规则,自动切换
避免写 if (process.env.NODE_ENV === 'mock') 这类硬编码开关:
- 开发阶段:用 Mock.js 在浏览器中拦截请求,规则写在 src/mock/index.ts,启动时自动加载
- 测试阶段:用 Vitest/Jest 的模块 mock + beforeEach/afterEach 统一管理生命周期
- 联调阶段:通过 webpack alias 或 vite config 将 api 模块指向 mock 版本,零代码修改切回真实请求
- 所有 mock 规则集中维护,命名带业务前缀(如 userMockRules、orderErrorScenarios),方便定位和复用


















