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
相关产品推荐
相关产品推荐

