1. 引言在 Android 或 Java 后端开发中对网络请求进行单元测试是保证代码质量的重要环节。MockWebServer是 OkHttp 团队提供的一个强大的测试工具它允许你在本地启动一个真实的 HTTP 服务器用于模拟网络请求和响应。然而随着 OkHttp 和测试框架的版本迭代其 API 和使用方式也在不断变化。本文将详细介绍实际开发中常用的 MockWebServer 版本、配套的测试框架并重点说明如何根据版本调整具体的 API 调用方式。2. 常用版本与环境配置2.1 MockWebServer 版本选择MockWebServer 通常作为okhttp的测试依赖引入。版本选择需与主项目使用的 OkHttp 版本保持一致以避免兼容性问题。最新稳定版推荐如okhttp:5.0.0-alpha.14对应的mockwebserver:5.0.0-alpha.14。广泛使用的版本okhttp:4.12.0及对应的mockwebserver:4.12.0。Android 项目需注意与 Android Gradle 插件及 Kotlin 版本的兼容性。Gradle 依赖示例Kotlin DSLdependencies { testImplementation(com.squareup.okhttp3:okhttp:4.12.0) testImplementation(com.squareup.okhttp3:mockwebserver:4.12.0) // 若使用 JUnit 5 testImplementation(org.junit.jupiter:junit-jupiter:5.10.0) }2.2 测试框架搭配MockWebServer 可与多种测试框架协同工作主流选择如下JUnit 4经典且稳定使用Before,After进行生命周期管理。JUnit 5 (Jupiter)现代选择支持更丰富的扩展模型使用BeforeEach,AfterEach。Kotlin 测试结合kotlin-test和kotlinx-coroutines-test进行协程测试。3. 核心 API 调用方式与版本适配3.1 服务器启动与关闭基础 API不同版本下启动和关闭服务器的 API 基本一致但资源管理方式略有不同。JUnit 4 示例import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before import org.junit.Test import java.io.IOException class MyUnitTest { private lateinit var server: MockWebServer Before Throws(IOException::class) fun setUp() { server MockWebServer() server.start() // 启动服务器默认使用随机端口 } After Throws(IOException::class) fun tearDown() { server.shutdown() // 关闭服务器释放资源 } Test fun testBasicRequest() { // 测试逻辑 val baseUrl server.url(/).toString() println(服务器运行在: $baseUrl) } }JUnit 5 示例使用 try-with-resources 风格import okhttp3.mockwebserver.MockWebServer import org.junit.jupiter.api.Test import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import java.io.IOException class MyUnitTestJUnit5 { private lateinit var server: MockWebServer BeforeEach fun setUp() { server MockWebServer() server.start() } AfterEach fun tearDown() { server.shutdown() } // 或者使用 use 扩展函数Kotlin 风格 Test fun test with use block() { MockWebServer().use { server - server.start() // 测试逻辑在此块内执行退出时自动 shutdown val baseUrl server.url(/).toString() println(自动管理资源的服务器: $baseUrl) } } }3.2 模拟响应Mock Response这是 MockWebServer 的核心功能。主要使用MockResponse类来构造期望的 HTTP 响应。通用方法各版本基本一致import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.junit.Test import kotlin.test.assertEquals class MockResponseTest { Test fun testMockResponse() { MockWebServer().use { server - server.start() // 1. 创建并配置一个模拟响应 val mockResponse MockResponse() .setResponseCode(200) // 状态码 .setBody({message: success}) // 响应体 .addHeader(Content-Type, application/json) // 响应头 // 2. 将响应加入服务器的队列 server.enqueue(mockResponse) // 3. 执行你的网络请求这里使用 OkHttp Client 示例 val client okhttp3.OkHttpClient() val request okhttp3.Request.Builder() .url(server.url(/api/test)) .build() val response client.newCall(request).execute() // 4. 断言响应符合预期 assertEquals(200, response.code) assertEquals({message: success}, response.body?.string()) // 5. 可选验证服务器接收到的请求 val recordedRequest: RecordedRequest? server.takeRequest() assertEquals(/api/test, recordedRequest?.path) } } }版本注意点MockResponse的链式调用 API 在 4.x 和 5.x 版本中保持稳定。但 5.x 版本可能引入新的构建器方法如对 HTTP/2 特性的更好支持建议查阅对应版本的官方文档。3.3 请求验证与获取RecordedRequest使用server.takeRequest()来获取服务器接收到的请求记录并进行验证。import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertTrue class RecordedRequestTest { Test fun testRecordedRequest() { MockWebServer().use { server - server.start() server.enqueue(MockResponse()) // 执行你的网络请求... val client okhttp3.OkHttpClient() val testRequest okhttp3.Request.Builder() .url(server.url(/user)) .post(okhttp3.RequestBody.create(text/plain, Hello Server)) .addHeader(X-Custom-Header, MyValue) .build() client.newCall(testRequest).execute() // 获取并验证请求 val recordedRequest: RecordedRequest server.takeRequest() assertEquals(POST, recordedRequest.method) assertEquals(/user, recordedRequest.path) assertEquals(MyValue, recordedRequest.getHeader(X-Custom-Header)) assertTrue(recordedRequest.body.readUtf8().contains(Hello Server)) } } }重要变更5.x 版本在 OkHttp 5.0.0-alpha.14 中RequestBody.create()等静态工厂方法可能已被弃用或移至扩展函数应使用“Hello Server”.toRequestBody(“text/plain”.toMediaType())这种更 Kotlin 友好的方式。务必根据你使用的具体 OkHttp 版本调整请求体的创建方式。3.4 调度器Dispatcher的高级用法对于复杂的测试场景如根据请求路径返回不同响应可以使用Dispatcher。OkHttp 4.x / 5.x 通用模式import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.junit.Test import kotlin.test.assertEquals class DispatcherTest { Test fun testDispatcher() { MockWebServer().use { server - server.start() val dispatcher object : Dispatcher() { override fun dispatch(request: RecordedRequest): MockResponse { return when (request.path) { /api/login - MockResponse() .setResponseCode(200) .setBody({token: abc123}) /api/profile - MockResponse() .setResponseCode(200) .setBody({name: Test User}) else - MockResponse().setResponseCode(404) } } } server.dispatcher dispatcher // 测试不同的路径 val client okhttp3.OkHttpClient() val loginResponse client.newCall( okhttp3.Request.Builder().url(server.url(/api/login)).build() ).execute() assertEquals(200, loginResponse.code) val profileResponse client.newCall( okhttp3.Request.Builder().url(server.url(/api/profile)).build() ).execute() assertEquals(200, profileResponse.code) val notFoundResponse client.newCall( okhttp3.Request.Builder().url(server.url(/unknown)).build() ).execute() assertEquals(404, notFoundResponse.code) } } }4. 针对不同测试框架的调整建议4.1 JUnit 4使用Rule简化管理需配合 OkHttp 的MockWebServerRule但该规则类在某些版本中可能不直接提供或已过时。更推荐显式地在Before/After中管理生命周期代码更清晰。4.2 JUnit 5利用BeforeEach,AfterEach。可以使用TestInstance(TestInstance.Lifecycle.PER_CLASS)配合BeforeAll,AfterAll来共享一个服务器实例需注意线程安全。结合 Kotlin 的use函数实现自动资源清理代码更简洁。4.3 Kotlin Coroutines 测试如果网络请求在协程中发起需使用kotlinx-coroutines-test来控制协程调度和时间。import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.MockResponse import org.junit.Test OptIn(ExperimentalCoroutinesApi::class) class CoroutineMockTest { Test fun testWithCoroutines() runTest { // 使用 runTest 控制测试协程 MockWebServer().use { server - server.start() server.enqueue(MockResponse().setBody({data: async})) // 假设有一个挂起函数发起请求 val result fetchDataFromServer(server.url(/).toString()) // 断言 result } } // 模拟的挂起函数 private suspend fun fetchDataFromServer(baseUrl: String): String { // 使用 OkHttp 的协程适配器或 Retrofit 进行调用 return 模拟的异步数据 } }5. 常见问题与排查端口冲突server.start()默认使用端口 0随机可用端口。如果指定端口且被占用会抛出IOException。建议始终使用随机端口。响应未入队如果测试代码执行了请求但服务器没有配置响应enqueue客户端调用会一直等待直到超时。确保每个预期的请求都预先enqueue了对应的MockResponse。请求顺序takeRequest()默认会阻塞直到收到请求。多个请求时响应的入队顺序应与请求的发送顺序一致或者使用Dispatcher进行更灵活的控制。HTTPSMockWebServer 默认使用 HTTP。测试 HTTPS 接口需要配置 SSL通常更复杂可以考虑在测试中暂时禁用 SSL 验证仅限测试环境或使用MockWebServer的useHttps(sslSocketFactory)方法。6. 总结MockWebServer 是进行网络层单元测试的利器。关键在于版本对齐确保mockwebserver依赖版本与项目中的okhttp版本一致。API 微调关注 OkHttp 5.x 等新版本中请求体构建等 API 的 Kotlin 化变更。框架适配根据项目使用的测试框架JUnit 4/5, Kotlin Test调整服务器的生命周期管理和测试结构。模式选择简单场景用enqueue复杂场景用Dispatcher。通过正确配置和调用你可以高效、可靠地模拟各种网络交互场景从而编写出健壮的网络相关单元测试。