Mock 概念与常见方案:MockWebServer、MockInterceptor、回放测试
说到 Mock,很多刚接触 Android 测试的同学第一反应就是「造假数据」。嗯,其实没那么简单。Mock 的核心目的是让测试与外部依赖解耦,让我们的单元测试或集成测试不再依赖网络环境、服务器状态或者第三方 SDK 的可用性。说白了,就是让被测代码在可控的「虚拟环境」里跑。
我当年在一个支付项目里就吃过亏——测试环境后端经常挂掉,导致 CI 里一半的用例都因为网络超时而失败。后来我用 Mock 把网络层全部替换掉,构建时间从 40 分钟降到了 8 分钟。所以这一章,我们先把 Mock 的几种主流方案理清楚。
1. 什么是 Mock?
Mock 不是「伪造」,而是「替代」。在 Android 测试中,我们通常需要模拟网络请求的响应、数据库的返回值、甚至是硬件传感器的数据。对于 Retrofit 测试来说,最核心的就是模拟 HTTP 请求的返回。
2. 三大常见方案
市面上能用来 Mock Retrofit 的工具不少,但最常用、最成熟的无非这三种:MockWebServer、MockInterceptor、以及我在这门课里要重点讲的回放测试。下面我一个一个拆开说。
2.1 MockWebServer
OKHttp 官方提供的测试工具。它会在本地启动一个真实的 HTTP 服务器,然后我们往里面塞预设的响应。测试时 Retrofit 的 baseUrl 指向这个本机地址(比如 http://localhost:8080)。
示例代码:
// 启动 MockWebServer
MockWebServer server = new MockWebServer();
server.start(8080);
// 预设响应
server.enqueue(new MockResponse()
.setResponseCode(200)
.setBody("{\"name\":\"test\",\"age\":18}")
);
// Retrofit 指向本地服务器
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(server.url("").toString())
.build();
// 调用接口
ApiService api = retrofit.create(ApiService.class);
Response<User> response = api.getUser().execute();
// 断言
assertEquals("test", response.body().name);
// 关闭
server.shutdown();
优点很突出:完全模拟真实 HTTP 交互,请求头、响应头、状态码都可以控制。缺点嘛,每次测试都要启动和关闭服务器,速度上会慢一些。而且如果你有几百个测试用例,每个用例都要写一堆 enqueue,维护成本挺高。
2.2 MockInterceptor
这是更轻量级的方式。MockInterceptor 本质上是一个 OkHttp 的 Interceptor,在请求发出去之前就被拦截了,直接返回预设的数据。它不需要启动服务器,速度更快。
我刚开始用这个方案的时候觉得特别爽,直到有一次……嗯,后面避坑环节再说。
// 创建 MockInterceptor
MockInterceptor interceptor = new MockInterceptor();
// 按条件返回
interceptor.addRule()
.urlContains("/user")
.method("GET")
.respondWith(new MockResponse()
.setStatusCode(200)
.setBody("{\"name\":\"test\"}")
);
// 添加到 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl("https://api.example.com/")
.build();
2.3 回放测试(本章核心)
前面两种方案都是「预先写好返回什么」,而回放测试是「先录制真实请求,然后播放相同的数据」。流程如下:
- 录制阶段:把应用的真实请求和响应抓下来,存成本地文件(比如 YAML 或 JSON)。
- 回放阶段:测试时读取本地文件,匹配请求,直接返回录制的响应。
这有什么好处?最大的好处是数据真实。你不需要手动构造返回体,也不会漏掉某些字段。我维护的一个电商 App,API 返回的字段有五六十个,如果用手动 Mock 简直要疯。用回放测试,录一次就能覆盖所有字段。
但回放测试也有门槛:你需要一个稳定的「录制器」工具,以及一套匹配逻辑(比如按 URL、请求体、header 匹配)。后面几章我会带你一步步搭出来。
3. 方案对比
| 维度 | MockWebServer | MockInterceptor | 回放测试 |
|---|---|---|---|
| 启动速度 | 慢(启动服务器) | 快(拦截器) | 中等(文件 IO) |
| 真实度 | 极高(完整 HTTP) | 高(但无实际网络) | 极高(真实流量) |
| 维护成本 | 高(每个用例写 enqueue) | 中等(规则匹配) | 低(录制一次) |
| 适合场景 | 少量异常测试 | 单元测试快速反馈 | 集成测试、回归测试 |
4. 避坑指南
我曾经有一次用 MockInterceptor 测支付回调,因为请求体里有个动态 timestamp,导致所有回放匹配都失败。后来我被迫在 Mock 规则里加了正则表达式忽略时间戳字段——这件事告诉我,Mock 不是无脑直接套的。
还有一次,MockWebServer 在 CI 环境里因为端口被占用导致整个构建失败。我当时直接懵了。后来改了端口随机分配,才稳定下来。
5. 本章知识图谱
下面的 SVG 把三种方案的关系和适用情况画出来了,你可以把这张图当作本章的思维导图。
6. 我的一些补充
最后说点个人经验。很多人一上来就选 MockWebServer,觉得「官方出品,必属精品」。但说实话,对于大部分业务逻辑测试,MockInterceptor 完全够用,而且快得多。回放测试适合那种 API 字段特别多的场景,或者你需要验证「历史版本的行为」——比如修了一个 bug,怕影响之前的功能。
如果你觉得这三种方案还不够,别急,后面十几章我会带着你手写一个回放框架,你会发现回放测试的威力远远不止于「录制-播放」这么简单。