如何在 OkHttp 中测试 IOException 情况?
How to test IOException case in OkHttp?
我正在尝试在 OkHttpClient 抛出 IOException 事件时测试我的代码库
待测代码
try (var response = okHttpClient.newCall(request).execute()) {
return response;
} catch (final IOException e) {
log.error("IO Error from API", e);
throw new ApiException(e.getMessage(), e);
}
测试
@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);
final var result = httpClient.createCustomer(request);
assertThatThrownBy(() -> httpClient.createCustomer(request))
.isInstanceOf(ApiException.class)
.hasMessage("IO Error from API");
}
我试图模拟 OkHttpClient
和 Builder
class,但是 Builder 是最终的,mockito 不能模拟它。
在被测class的构造函数中,建议通过调用this
创建一个新的OkHttpClient实例
this.okHttpClient = okHttpClient.newBuilder().build();
我试图围绕 OkHttpClient 创建一个包装器,但这也没有用
public class OkHttpClientWrapper extends OkHttpClient {
private OkHttpClient okHttpClient;
public OkHttpClientWrapper(final OkHttpClient okHttpClient) {
this.okHttpClient = okHttpClient;
}
@Override
public OkHttpClient.Builder newBuilder() {
return new Builder(okHttpClient);
}
}
}
如何强制 okhttpclient 抛出 IOException?
您有几个选择:
- 使用Powermock with Mockito模拟最后的元素。这将允许您对所有代码进行单元测试。
- 您可以使用 Wiremock or a similar over-the-wire HTTP mocking tool which will allow you to test all of your code in a black-box fashion. All of your code can be tested this way as a black box.
- 重构你的代码并将 OkHttp 类 和方法包装在你自己的 类 中,可以被模拟。这仍然留下一些未经测试的代码。
最终最好的解决方案是利用 OkHttp 的 MockWebServer
https://github.com/square/okhttp/tree/master/mockwebserver
使用 MockWebserver 意外终止 HTTP 连接的场景,导致 IOException 场景
@Test
void createCustomer_WhenValidRequestAndServerTerminatesConnection_ThenThrowIOException() throws ZeusServiceException, ZeusClientException {
//Given
final CreateCustomerRequest request = CreateCustomerRequest.builder().build();
//When
mockWebServer.enqueue(new MockResponse()
.setBody(new Buffer().write(new byte[4096]))
.setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY));
assertThatThrownBy(() -> httpClient.createCustomer(request))
.isInstanceOf(IOException.class)
.hasMessage("unexpected end of stream");
}
注意:本次测试使用AssertJ库
我正在尝试在 OkHttpClient 抛出 IOException 事件时测试我的代码库
待测代码
try (var response = okHttpClient.newCall(request).execute()) {
return response;
} catch (final IOException e) {
log.error("IO Error from API", e);
throw new ApiException(e.getMessage(), e);
}
测试
@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);
final var result = httpClient.createCustomer(request);
assertThatThrownBy(() -> httpClient.createCustomer(request))
.isInstanceOf(ApiException.class)
.hasMessage("IO Error from API");
}
我试图模拟 OkHttpClient
和 Builder
class,但是 Builder 是最终的,mockito 不能模拟它。
在被测class的构造函数中,建议通过调用this
创建一个新的OkHttpClient实例 this.okHttpClient = okHttpClient.newBuilder().build();
我试图围绕 OkHttpClient 创建一个包装器,但这也没有用
public class OkHttpClientWrapper extends OkHttpClient {
private OkHttpClient okHttpClient;
public OkHttpClientWrapper(final OkHttpClient okHttpClient) {
this.okHttpClient = okHttpClient;
}
@Override
public OkHttpClient.Builder newBuilder() {
return new Builder(okHttpClient);
}
}
}
如何强制 okhttpclient 抛出 IOException?
您有几个选择:
- 使用Powermock with Mockito模拟最后的元素。这将允许您对所有代码进行单元测试。
- 您可以使用 Wiremock or a similar over-the-wire HTTP mocking tool which will allow you to test all of your code in a black-box fashion. All of your code can be tested this way as a black box.
- 重构你的代码并将 OkHttp 类 和方法包装在你自己的 类 中,可以被模拟。这仍然留下一些未经测试的代码。
最终最好的解决方案是利用 OkHttp 的 MockWebServer
https://github.com/square/okhttp/tree/master/mockwebserver
使用 MockWebserver 意外终止 HTTP 连接的场景,导致 IOException 场景
@Test
void createCustomer_WhenValidRequestAndServerTerminatesConnection_ThenThrowIOException() throws ZeusServiceException, ZeusClientException {
//Given
final CreateCustomerRequest request = CreateCustomerRequest.builder().build();
//When
mockWebServer.enqueue(new MockResponse()
.setBody(new Buffer().write(new byte[4096]))
.setSocketPolicy(SocketPolicy.DISCONNECT_DURING_RESPONSE_BODY));
assertThatThrownBy(() -> httpClient.createCustomer(request))
.isInstanceOf(IOException.class)
.hasMessage("unexpected end of stream");
}
注意:本次测试使用AssertJ库