Mock测试工具概览:Mockito、MockWebServer、WireMock
说实话,刚开始做接口Mock的时候,我也纠结过:到底用哪个工具?
三个工具各有各的脾气。Mockito、MockWebServer、WireMock,名字听起来挺像,但用起来完全不是一回事。我最初入坑时,在同一个项目里来回切换,闹了不少笑话。
这一节,我就把自己摸索出来的心得掰开揉碎讲给你。
核心认知:三种工具服务于不同的Mock场景。选错工具,代码会很难维护。
三者的核心定位
先简单说清楚它们各自是干什么的:
| 工具 | 定位 | 作用层次 | 运行方式 | 是否支持HTTP接口Mock |
|---|---|---|---|---|
| Mockito | Java单元测试Mock框架 | 代码层面(对象、方法) | 内嵌在JVM中 | 不支持(需配合其他工具) |
| MockWebServer | 轻量级HTTP服务器Mock | HTTP请求/响应层 | 启动本地Socket服务 | 支持(专为OkHttp/Retrofit设计) |
| WireMock | HTTP API Mock & 录制框架 | HTTP请求/响应层 | 独立进程或内嵌启动 | 支持(功能强大且完善) |
你看这个表,就清楚多了。Mockito管的是代码里的对象行为,MockWebServer和WireMock管的是网络请求。很多人一上来就用Mockito去Mock网络请求,结果发现根本拦截不了——我最初也犯过这个错。
选择口诀:单测Mock用Mockito,接口Mock看场景——轻量测试选MockWebServer,功能需求上WireMock。
Mockito:单元测试的瑞士军刀
Mockito是我用得最多的Mock框架。它解决的是对象依赖问题。你想想看,一个Service里调了三个Repository,单元测试时总不能真的去连数据库吧?这时候Mockito就派上用场了。
我第一次用Mockito是在一个小型支付项目里。当时要测试订单状态流转,但无法实际调用支付网关。Mockito把网关客户端Mock掉后,我可以模拟各种回调场景——支付成功、支付失败、超时重试,一口气全测了。
代码看起来是这样:
// Mockito 核心用法
@Mock
private PaymentGateway paymentGateway;
@InjectMocks
private OrderService orderService;
@Test
void testPaymentSuccess() {
// 模拟:调用网关返回成功
when(paymentGateway.charge(any())).thenReturn(new PaymentResult(true, "SUCCESS"));
Order order = orderService.processPayment(new Order("123", 99.9));
assertEquals(OrderStatus.PAID, order.getStatus());
}
嗯,这里要注意:Mockito只适合Mock代码内部的对象依赖。它管不了网络请求,因为网络请求是跨进程的。很多新手在这上面栽跟头。
MockWebServer:专治接口联调的利器
再来说说MockWebServer。这是Square公司(也就是OkHttp亲爹)推出的测试工具。它与OkHttp和Retrofit配合得天衣无缝。说白了,它在本地起一个真正的HTTP服务器,然后让你的Retrofit请求打到这个服务器上。
我记得有一次项目,服务端接口还没写好,但前端已经催着联调了。我把MockWebServer一开,定义好几个接口的Mock响应,前后端同时在Mock数据上跑通流程,效率提升不是一星半点。
这是它的基本用法:
// MockWebServer 核心用法
MockWebServer mockServer = new MockWebServer();
mockServer.start();
// 配置Retrofit指向Mock服务器
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mockServer.url("/"))
.build();
// 模拟接口响应(超时、错误码都能模拟)
mockServer.enqueue(new MockResponse()
.setResponseCode(200)
.setBody("{\"status\":\"ok\"}")
.setBodyDelay(500, TimeUnit.MILLISECONDS));
// 还可以模拟异常场景
mockServer.enqueue(new MockResponse()
.setResponseCode(500)
.setBody("{\"error\":\"internal error\"}"));
// 最后要记得关闭
mockServer.shutdown();
避坑指南:我之前遇到过一个问题——MockWebServer默认端口是动态分配的,如果硬编码端口号会导致测试在不同机器上跑不过。务必用mockServer.url("/")动态获取baseUrl。
MockWebServer的优势在哪?轻量、快、开箱即用。它不需要额外配置,不需要准备文件,几行代码就完事了。
但它的缺点也很明显:不支持录制回放,Mock数据都在代码里写死,维护起来比较费劲。
WireMock:契约测试的瑞士军刀
最后聊聊WireMock。我个人觉得,它是三者中最「重型」的一个,但也最「强大」。
WireMock像是一个完整的Mock服务器框架。它支持:
- Stub配置:通过JSON或代码定义Mock响应
- 请求匹配:支持URL、Header、Body、Query参数精准匹配
- 录制回放:这是它最大的卖点——先录制真实接口,再回放做比对
- 独立运行:可以启动一个WireMock进程,供团队共用
我曾在项目中用WireMock模拟第三方支付接口。对方接口不稳定,时不时飘个超时,每次测试都要等。我把他们的真实响应录制下来,然后在本地回放,测试稳定性从60%直接提升到99%。
配置示例:
// WireMock 核心用法
// 1. 启动WireMock服务器
WireMockServer wireMockServer = new WireMockServer(8080);
wireMockServer.start();
// 2. 配置Stub(支持JSON配置,也可以写在代码里)
stubFor(get(urlEqualTo("/api/user/123"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\":123,\"name\":\"Alice\"}")
));
// 3. 录制模式(记一次就够了)
// 启动时带 --record-mappings 参数
// 访问真实接口,自动生成mapping文件
经验之谈:WireMock的录制功能特别适合契约测试。我和服务端团队约定好接口契约,把契约文件直接转成WireMock的mapping,服务端重构时,我的测试一秒都不用改。
怎么选?我给的建议
聊到这里,你应该明白了——这三个工具不是替代关系,而是互补关系。在实际项目中,我通常是混合使用的:
| 测试场景 | 推荐工具 | 理由 |
|---|---|---|
| 单元测试(Service/Repository层) | Mockito | 快速隔离外部依赖 |
| Retrofit接口联调与基础测试 | MockWebServer | 轻量、与OkHttp深度集成 |
| 复杂场景模拟(超时、错误码组合) | MockWebServer | 精确控制每次响应 |
| 契约测试 / 接口录制回放 | WireMock | 录制功能强大、可脱离代码 |
| 集成测试(需Mock完整API服务) | WireMock | 支持独立进程部署 |
小建议:如果你和我一样懒,建议优先掌握MockWebServer。因为它的学习成本最低,一个类搞掂所有。等遇到需要录制回放的场景时,再补上WireMock也不迟。
为什么要这样安排?你想想看,不同阶段用不同的工具——单元测试、接口测试、契约测试,它们根本不是一个层面的事。非要在一个工具上吊死,反而会把事情搞复杂。
举个例子,我见过有人用Mockito里面的when()去模拟OkHttp的Call对象,绕了一大圈,代码又脏又难维护。其实换成MockWebServer,两行代码就解决问题了。
所以,别纠结谁更好。把这些工具当工具,该用哪个用哪个。
特别注意:如果我需要在同一个测试类里同时使用Mockito和MockWebServer,千万要注意初始化顺序。MockWebServer必须先启动,否则Retrofit实例化时会找不到baseUrl。这个顺序问题,我曾经排查了一下午才找到原因。
嗯,工具概览就讲到这里。记住核心一点:不同层次用不同工具,不要张冠李戴。接下来我们会深入到MockWebServer的具体用法中去,直接上手写代码。