You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用MockWebServer仅测试Retrofit请求参数而不执行请求?

解决方案

当然可以!这里有几个不同的方案,你可以根据自己的测试场景选择:

1. 返回最小合法的JSON响应(最简单的方案)

Retrofit需要能把响应解析成TimeResponse对象,但你完全不需要返回真实的大数据——只需要返回满足对象结构的最小合法JSON即可。比如假设你的TimeResponse是这样定义的:

data class TimeResponse(
    val iso: String,
    val epoch: Double
)

那你只需要返回包含必填字段的极简JSON,比如:

{"iso": "dummy", "epoch": 0.0}

这样既满足了解析要求,又不用处理庞大的真实数据,同时你可以专注于验证请求的路径、请求头等信息。修改后的测试代码如下:

@Test fun validate_time_request() = runBlocking {
    // 用极简JSON代替真实大数据
    val minimalValidResponse = """{"iso": "dummy", "epoch": 0.0}"""
    mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(minimalValidResponse))
    
    // 仍然需要调用API触发请求,但响应是极简的,解析成本极低
    createClient().time()
    
    // 重点验证请求负载
    val recordedRequest = mockWebServer.takeRequest()
    assertThat(recordedRequest.path).isEqualTo("/time/")
    assertThat(recordedRequest.method).isEqualTo("GET")
    // 如果有请求头需要验证,比如Authorization、Accept等
    // assertThat(recordedRequest.getHeader("Accept")).isEqualTo("application/json")
}

2. 自定义Converter跳过响应解析(适合完全不想写JSON的场景)

如果你连极简JSON都不想写,可以自定义一个Retrofit的Converter,让它在解析特定接口的响应时直接返回一个默认的TimeResponse实例,这样MockWebServer可以返回任意内容(甚至空响应)。

首先创建一个包装原Converter的自定义Converter:

class SkipTimeResponseConverter(
    private val delegate: Converter<ResponseBody, TimeResponse>
) : Converter<ResponseBody, TimeResponse> {
    override fun convert(value: ResponseBody): TimeResponse {
        // 直接返回默认实例,跳过真实解析
        return TimeResponse(iso = "dummy", epoch = 0.0)
    }
}

然后在创建Retrofit客户端时替换对应的Converter:

private fun createClient(): AppApi {
    val gson = Gson()
    val retrofitBuilder = Retrofit.Builder()
        .baseUrl(mockWebServer.url("/").toString())
        .addConverterFactory(object : Converter.Factory() {
            override fun responseBodyConverter(
                type: Type,
                annotations: Array<out Annotation>,
                retrofit: Retrofit
            ): Converter<ResponseBody, *>? {
                // 只对TimeResponse类型的响应使用自定义Converter
                return if (type == TimeResponse::class.java) {
                    val originalConverter = GsonConverterFactory.create(gson)
                        .responseBodyConverter(type, annotations, retrofit) as Converter<ResponseBody, TimeResponse>
                    SkipTimeResponseConverter(originalConverter)
                } else {
                    // 其他类型用原Converter
                    super.responseBodyConverter(type, annotations, retrofit)
                }
            }
        })
    return retrofitBuilder.build().create(AppApi::class.java)
}

现在测试时MockWebServer可以直接返回空响应:

@Test fun validate_time_request_with_empty_response() = runBlocking {
    mockWebServer.enqueue(MockResponse().setResponseCode(200)) // 空响应也能正常解析
    
    createClient().time()
    
    val recordedRequest = mockWebServer.takeRequest()
    assertThat(recordedRequest.path).isEqualTo("/time/")
    // 其他请求验证逻辑
}

3. 直接验证Retrofit构建的请求(无需发送请求)

如果你根本不需要发送请求,只是想验证Retrofit根据接口注解构建的请求是否正确,可以把接口改成返回Call<TimeResponse>,然后直接获取请求对象进行验证,完全不需要MockWebServer:

// 修改接口定义
interface AppApi { 
    @GET("/time/") 
    fun time(): Call<TimeResponse> 
}

// 测试代码
@Test fun verify_request_build_logic() {
    val call = createClient().time()
    val request = call.request()
    
    assertThat(request.url().encodedPath()).isEqualTo("/time/")
    assertThat(request.method()).isEqualTo("GET")
    // 验证请求头
    assertThat(request.header("Accept")).isEqualTo("application/json")
}

这种方法适合仅验证接口注解(比如路径、HTTP方法、请求头)的场景,不需要涉及任何网络请求模拟。


内容的提问来源于stack exchange,提问作者anticafe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:52:27