Mock Server:契约、场景与故障模拟

Mock Server:契约、场景与故障模拟

后端接口还没完成时,前端最容易先在组件里写一份固定 JSON。页面很快能跑起来,但这份数据没有请求过程,也不会出现延迟、错误和权限差异。等到真实接口接入,很多问题才第一次出现。

Mock Server 更有价值的地方,是在网络边界上表现得像真实服务。前端仍然发送正常请求,经过序列化、请求封装和缓存层,再收到符合约定的状态码、响应头与数据。这样验证的是整条数据链路,而不只是组件能不能渲染一份对象。

Mock 应该从接口契约出发

Mock 的路径、方法、参数和响应结构应该来自同一份 API 契约。服务端把字段从必填改为可选,Mock 也要同步变化;否则前端可能一直在为一个已经不存在的接口开发。

有 OpenAPI、GraphQL Schema 或 IDL 时,可以用它生成基础处理器和数据类型,但生成结果只是起点。分页边界、权限差异、错误条件和数据之间的关系仍然需要按业务补充。接口契约与生成流程见 API 契约与类型生成。

契约校验也可以反过来运行:让 Mock 的响应接受 Schema 检查,阻止不合法的示例进入开发环境。不过它仍然不能证明真实服务实现正确,前后端最终还需要集成测试或契约测试确认两边使用的是同一种语义。

场景比随机数据更容易复现问题

只返回一组正常数据,覆盖不了页面的大部分状态。一个接口通常至少会遇到空结果、字段缺失、权限不足、服务端错误、响应缓慢和分页结束。文件上传、流式响应或乐观更新还会有自己的中间状态。

这些情况更适合定义成有名字的场景,例如“没有订单”“令牌已过期”或“第二页加载失败”。开发者和自动化测试都可以选择同一个场景,失败时也能准确复现。

随机生成数据适合扩大形状覆盖,但不适合成为默认行为。测试每次拿到不同内容,排序、快照和边界错误就会变得不稳定。如果确实需要随机数据,应固定种子,并在失败信息中记录它。

延迟和失败需要有控制地模拟

真实网络不会永远立即成功。适当延迟可以暴露 Loading 闪烁、按钮重复提交和请求竞态;超时与断网能验证取消和重试;不同状态码则能检查错误是否被正确映射。

延迟不宜在所有请求上随意波动,否则本地开发会变慢,自动化测试也更容易偶发失败。更合适的做法是默认快速且确定,只在专门场景中增加延迟、抖动或故障。

错误也要发生在正确位置。返回 200 再在响应体里放一个假的错误字段,无法验证 HTTP Client 对 401、404 或 500 的实际处理。模拟网络错误时,则不应该同时构造一份服务端响应。

写操作需要一份可重置的状态

如果创建请求总是返回成功,却没有把新记录加入后续列表,端到端流程很快会失真。较完整的 Mock Server 可以在内存中维护数据,让新增、修改和删除影响之后的读取。

这份状态要能在每个测试开始前恢复到已知基线。测试之间共享并持续累积数据,会让结果依赖执行顺序。本地开发可以允许手动编辑,但重置入口和初始数据仍要明确。

时间和标识符也属于可重复性的一部分。依赖当前时间、随机 ID 或自动增长顺序的场景,最好允许测试注入固定值,不要让断言随着运行时刻变化。

在网络层拦截通常更容易复用

Mock 可以作为独立服务运行,也可以在浏览器或测试进程中拦截网络请求。Mock Service Worker 这类工具保持应用的调用方式不变,因此同一组处理器可以服务本地开发、组件测试和部分端到端测试。

直接替换业务 API 模块更适合小范围单元测试,速度快,依赖也少。但它绕过了请求序列化、拦截器、状态码和响应解析,不能代替网络边界上的验证。两种方式可以同时使用,只要清楚每层测试没有覆盖什么。

Mock 不能成为第二套后端

Mock Server 越真实,维护成本越高。把完整业务规则复制一遍,会得到一个看似准确、实际与生产环境独立演进的后端。Mock 更适合实现前端依赖的可观察行为,不负责证明复杂领域计算正确。

真实接口可用后,关键流程仍要经过集成环境。CI 也可以检查契约是否变化、Mock 是否仍符合 Schema,并定期删除已经没人使用的场景。目标不是让前端永远脱离后端,而是在依赖暂时不可用或故障难以制造时,仍能稳定推进和验证界面。

生产构建还应默认关闭 Mock,并在配置错误时直接失败。一个悄悄回退到假数据的线上页面,比接口明确报错更难发现。