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

Mockito模拟OkHttp首次调用正常,二次调用响应体为空问题排查

Mockito模拟OkHttp时第二次调用响应体为空

我用Mockito模拟OkHttp类,首次调用Mock正常,能获取到响应体内容"foo",但第二次调用时响应体为空。调试后发现Mock似乎在正常工作,却不清楚为何第二次调用失效。

测试代码

@Test
void get500() throws IOException {
    String responseMessage = "foo";
    int responseCode = 500;

    final OkHttpClient okHttpClient = mock(OkHttpClient.class);
    final Call remoteCall = mock(Call.class);
    final Response responseBuilder = new Response.Builder()
            .request(new Request.Builder().url(url).build())
            .protocol(Protocol.HTTP_1_1)
            .code(responseCode).message("").body(
                    ResponseBody.create(
                            responseMessage,
                            MediaType.parse("application/json")
                    ))
            .build();

    when(remoteCall.execute()).thenReturn(responseBuilder);
    when(okHttpClient.newCall(any())).thenReturn(remoteCall);
    
    MyHttp myHttp = new MyHttp(okHttpClient);
    ResponseServer response = myHttp.get("http://url.com");
    Assertions.assertEquals(response.getBody(), responseMessage);
}

MyHttp类代码

public class MyHttp {
    protected final OkHttpClient client;
    
    public MyHttp(OkHttpClient client) {
        this.client = client;
    }
    
    public ResponseServer get(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        
        Response response = client.newCall(request).execute();
        String responseBody = Objects.requireNonNull(response.body()).string();
        Assertions.assertEquals(response.getBody(), "foo");

        Response response2 = client.newCall(request).execute();
        String responseBody2 = Objects.requireNonNull(response2.body()).string();
        Assertions.assertEquals(response2.getBody(), "foo");  // response is empty
    }
}

补充尝试(未解决问题)

Response mockResponse = mock(Response.class);
when(mockResponse.request()).thenReturn(new Request.Builder().url(url).build());
when(mockResponse.protocol()).thenReturn(Protocol.HTTP_1_1);
when(mockResponse.code()).thenReturn(responseCode);
when(mockResponse.message()).thenReturn("");
when(mockResponse.body()).thenReturn(
        ResponseBody.create(
                responseMessage,
                MediaType.parse("application/json")
        ));

问题原因

核心问题在于OkHttp的ResponseBody的string()方法是一次性的:调用该方法时会读取响应体的字节流并将其关闭,后续再尝试获取响应体内容时,流已经耗尽,自然返回空。

你当前的测试代码中,remoteCall.execute()每次返回的都是同一个Response实例,第一次调用string()已经把该实例的ResponseBody流耗尽,第二次调用时即使拿到同一个Response,body的内容也已经为空。

解决方案

方案1:每次调用execute()返回全新的Response实例

使用thenAnswer动态生成新的Response对象,确保每次调用execute()都能拿到带有全新ResponseBody的Response:

when(remoteCall.execute()).thenAnswer(invocation -> {
    return new Response.Builder()
            .request(new Request.Builder().url(url).build())
            .protocol(Protocol.HTTP_1_1)
            .code(responseCode).message("")
            .body(ResponseBody.create(responseMessage, MediaType.parse("application/json")))
            .build();
});

方案2:Mock ResponseBody的string()方法,使其重复返回指定内容

直接Mock ResponseBody,让string()方法每次调用都返回预设的内容,避免流被耗尽的问题:

ResponseBody mockBody = mock(ResponseBody.class);
when(mockBody.string()).thenReturn(responseMessage);
// 需同时Mock contentType(),避免后续代码抛出空指针
when(mockBody.contentType()).thenReturn(MediaType.parse("application/json"));

final Response responseBuilder = new Response.Builder()
        .request(new Request.Builder().url(url).build())
        .protocol(Protocol.HTTP_1_1)
        .code(responseCode).message("")
        .body(mockBody)
        .build();

when(remoteCall.execute()).thenReturn(responseBuilder);

内容的提问来源于stack exchange,提问作者Gabriel Muñumel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:17:11