如何用MockWebServer触发Retrofit中body().string()的IOException
问题原因分析
你之前使用SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY无法达成预期效果的核心原因是异常触发时机不匹配:
- 该Socket策略的执行逻辑是:MockWebServer写完所有响应头内容后,立刻断开socket连接
- 此时OkHttp的HTTP层在完成响应头解析、将Response对象封装为Retrofit回调参数之前,就会感知到连接断开,直接把异常抛到Call执行层,触发
onFailure回调,根本不会进入你写的onResponse方法内部的try-catch逻辑。
正确实现方式
要达成预期效果,核心要控制两个关键时机:
- 服务端必须完整推送所有200状态的响应头到客户端,让OkHttp成功解析响应、触发
onResponse回调,绝对不能在响应头传输阶段断开连接 - 必须等业务代码进入
onResponse方法、执行到response.body().string()的读取逻辑时,才抛出IOException
可落地的实现代码
不用内置的DISCONNECT类SocketPolicy,通过自定义响应体的写入逻辑精准控制异常触发时机:
MockResponse mockResponse = new MockResponse() .setResponseCode(200) .setHeader("Content-Type", "application/json") .setBody(new ResponseBody() { @Override public MediaType contentType() { return MediaType.get("application/json"); } @Override public long contentLength() { return -1; } @Override public void writeTo(BufferedSink sink) throws IOException { // 先把缓冲区里的响应头内容全部推给客户端,确保客户端能完整接收响应头、触发onResponse回调 sink.flush(); // 短暂等待,保证客户端完成onResponse的调用流程,进入body读取逻辑 try { Thread.sleep(100); } catch (InterruptedException e) { throw new IOException("模拟连接中断", e); } // 此时再抛出IO异常,会在response.body().string()的调用过程中触发 throw new IOException("模拟读取响应体时的连接断开"); } });
注意:Thread.sleep的等待时长可以根据实际测试环境调整,只要保证客户端已经收到完整响应头、并触发onResponse回调即可,避免等待时间过短导致异常还是在回调流程之前抛出。
流程验证
按照这个配置执行时:
- OkHttp收到完整的响应头后,会正常创建Response对象并触发
onResponse,不会进入onFailure - 业务代码判断
response.isSuccessful()为true,调用response.body().string()时,才会读取自定义响应体的内容,此时抛出的IOException会被onResponse内部的catch块捕获,完全符合测试预期。
内容的提问来源于stack exchange,提问作者CaHa
相关产品推荐
相关产品推荐

