一、认识 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 测试知识结构 Retrofit 真实 HTTP 调用 Mock 测试 生产环境 测试环境 MockWebServer 录制回放 MockInterceptor 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。后面章节会逐步引入录制工具、回放机制、以及各种边界情况的处理。