一、认识 Retrofit 与 Mock 测试
我们先从最基础的问题聊起。
你在用 Retrofit 做网络请求时,有没有想过——
“如果后端接口还没写好,我怎么调试 UI?”
“写单元测试时,每次都要真的发 HTTP 调用,慢得要死,怎么办?”
嗯,这两个问题的答案,就是今天的主角:Mock 测试。
1.1 什么是 Mock?
Mock 直译过来是“模拟”、“仿造”。在软件测试里,它指的是用假的、可控的对象,代替真实的外部依赖。
拿 Retrofit 来说,真实依赖就是远程服务器上的 API。Mock 就是造一个“假的服务器”,我们想要它返回什么数据,它就返回什么。
Mock 的核心思想:让测试只关注你写的代码,而不是网络、数据库这些不可控的东西。
我记得刚入行时,写接口测试直接对着线上环境跑。有一次不小心把测试数据写进了生产库…… 从那以后,我再也不敢让测试碰到真实后端了。
1.2 为什么需要 Mock?
说白了,真实开发中你遇到的坑,远比文档里写的多。
- 后端进度落后:接口文档写好了,但接口还没上线。前端只能干等吗?
- 测试环境不稳定:点一下按钮,等 3 秒才返回结果,甚至报 502。这种环境怎么跑自动化测试?
- 极端场景难模拟:比如超时、网络中断、服务器 500。你总不能每次真的去拔网线吧?
- 测试速度太慢:一次真实的 HTTP 请求几百毫秒,几千个测试用例跑下来,下班都跑不完。
我以前带团队时,遇到过最夸张的情况:测试环境数据库每天凌晨 4 点自动重启,早上 9 点同事们一来,全 bug。当时我们用了 Mock 方案,把依赖数据库的接口全部换成模拟数据,才让 CI 流水线稳定下来。
1.3 Retrofit + Mock 能解决什么?
Retrofit 本身是一个类型安全的 HTTP 客户端,它通过注解声明 API 接口。而 Mock 测试,就是在不启动真正服务器的情况下,让 Retrofit 调用你预设的假数据。
组合起来的效果很直接:
- 开发阶段:本地快速调试,不用依赖后端是否启动
- 单元测试:毫秒级完成,覆盖超时、异常、空数据等边界
- 集成测试:控制返回顺序,回放之前录制的真实响应
一个小技巧:我个人习惯在项目里保留两套接口实现 —— 一套真实 OkHttpClient,一套 MockInterceptor。用一个常量开关就能切换,开发期间永远开着 Mock 模式,调试效率翻倍。
1.4 Mock 测试的几种常见方式
在 Android / Java 生态下,做 Retrofit Mock 测试常见有三种做法。我把它们整理成一个表格,方便你对比:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 硬编码 MockInterceptor | 在 OkHttp 层添加拦截器,拦截请求直接返回固定 JSON 字符串 | 简单直接,零依赖 | 维护成本高,数据写死在代码里 |
| MockWebServer | Square 官方库,本地启动一个虚拟 HTTP 服务器,接收真实请求 | 支持断言请求,可模拟延迟/异常 | 需要额外启动服务器,相对重一些 |
| 录制回放(Cassette) | 第一次运行录制真实响应,后续运行使用本地缓存的数据 | 数据真实,可离线运行 | 数据过期需重新录制 |
这三种里面,我最早用的是 MockWebServer。但后来项目接口越来越多,发现每次手动写 response 太累了。于是转向了“录制回放”的思路 —— 这也是咱们这门课程的重点。
1.5 知识体系总览
在正式开始动手之前,先对整个 Mock 测试的知识结构有个印象。下面这张图概括了本节课的核心概念和关系:
从图中可以清晰看到:Retrofit 在开发/测试中面临两条路 —— 要么真实调用(依赖后端),要么走 Mock。Mock 方式里,我们重点学习 MockInterceptor 和 录制回放 两种实现。
1.6 本章核心要点
- Mock 是什么:用模拟对象替换真实依赖,让测试独立可控
- 为什么需要:解决接口未就绪、环境不稳定、极端场景难模拟、测试速度慢
- 怎么做:三种主流方案,本课程聚焦“MockInterceptor + 录制回放”
避坑指南:我曾经见过团队直接在 Retrofit 的接口实现类里写死 JSON 字符串来做 Mock。这种做法一旦接口变动,全局搜索替换 JSON 很痛苦。更合理的做法是在 OkHttp 拦截器层做拦截,与应用层代码解耦。
1.7 简单示例热热身
在真正动手前,先看一段最简单的 MockInterceptor 代码,感受一下:
class MockInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 判断请求路径,返回对应模拟数据
return when {
request.url.encodedPath == "/api/users" -> {
Response.Builder()
.code(200)
.message("OK")
.request(request)
.protocol(Protocol.HTTP_1_1)
.body("""{"users": [{"id": 1, "name": "Mock张三"}]}""".toResponseBody())
.build()
}
else -> chain.proceed(request)
}
}
}
嗯,就这么简单。把这段 Interceptor 加到 OkHttpClient 里,Retrofit 发起的任何请求都会被它拦截,返回你指定的 JSON。
当然,真实项目里我们不会这样手动写 JSON。后面章节会逐步引入录制工具、回放机制、以及各种边界情况的处理。