如何在OkHttp中测试IOException抛出的业务场景?
解决方案
你遇到的核心问题是OkHttpClient及其Builder都是final类,Mockito默认不支持mock final类,可通过以下三种方案解决:
方案1:启用Mockito对final类的mock支持
这是改动最小的方案,不需要修改业务代码,仅需调整测试配置和测试代码:
- 在测试资源路径
src/test/resources下创建目录结构META-INF/mockito-extensions,新增文件org.mockito.plugins.MockMaker,文件内容写入:
mock-maker-inline
- 修正你的测试代码,删除提前触发方法调用的多余行:
@Test void createCustomer_WhenValidRequestAndIOException_ThenThrowAPIException() throws ZeusServiceException, ZeusClientException { //Given final OkHttpClient okHttpClientMock = mock(OkHttpClient.class, RETURNS_DEEP_STUBS); final OkHttpClient.Builder okHttpBuilderMock = mock(OkHttpClient.Builder.class); httpClient = new HttpClient(okHttpClientMock, configuration, objectMapper); //When when(okHttpClientMock.newBuilder()).thenReturn(okHttpBuilderMock); when(okHttpBuilderMock.build()).thenReturn(okHttpClientMock); when(okHttpClientMock.newCall(any())).thenThrow(IOException.class); // 校验抛出异常 assertThatThrownBy(() -> httpClient.createCustomer(request)) .isInstanceOf(ApiException.class) .hasCauseInstanceOf(IOException.class); }
注意:你之前的断言hasMessage("IO Error from API")是错误的,因为你抛出的ApiException的message是e.getMessage(),也就是IOException的消息,不是日志里打印的内容,建议改为校验异常类型或者cause。
方案2:使用OkHttp官方的MockWebServer(更推荐)
这是OkHttp官方提供的测试组件,不需要mock任何类,更贴近真实运行场景:
- 引入MockWebServer依赖(以Gradle为例):
testImplementation 'com.squareup.okhttp3:mockwebserver:你的okhttp版本号'
- 编写测试代码:
@Test void createCustomer_WhenValidRequestAndIOException_ThenThrowAPIException() throws IOException { // 启动MockWebServer MockWebServer mockWebServer = new MockWebServer(); // 配置收到请求后直接断开连接,触发IOException mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST)); mockWebServer.start(); // 将你的configuration中的API base地址改为mockWebServer的地址 when(configuration.getApiBaseUrl()).thenReturn(mockWebServer.url("/").toString()); // 初始化HttpClient,使用真实的OkHttpClient实例即可 httpClient = new HttpClient(new OkHttpClient(), configuration, objectMapper); // 断言 assertThatThrownBy(() -> httpClient.createCustomer(request)) .isInstanceOf(ApiException.class) .hasCauseInstanceOf(IOException.class); // 关闭服务 mockWebServer.shutdown(); }
方案3:抽象Http客户端接口解耦
如果要彻底降低业务代码和OkHttp的耦合,可自行抽象一层Http客户端接口:
- 定义接口:
public interface CustomHttpClient { Response execute(Request request) throws IOException; }
- 实现OkHttp的适配类:
public class OkHttpCustomClient implements CustomHttpClient { private final OkHttpClient okHttpClient; public OkHttpCustomClient(OkHttpClient okHttpClient) { this.okHttpClient = okHttpClient.newBuilder().build(); } @Override public Response execute(Request request) throws IOException { return okHttpClient.newCall(request).execute(); } }
- 你的业务HttpClient类改为依赖
CustomHttpClient接口,测试时直接mock接口抛IOException即可,不需要处理OkHttp的final类问题。
内容的提问来源于stack exchange,提问作者tomaytotomato
相关产品推荐
相关产品推荐

