部分解决方案是在模拟 HttpClient 上模拟执行 ODataQuery。
首先,原始方法需要分成两个独立的部分,一个用于构建查询,另一个用于执行它。无论如何,这是一个很好的设计,所以没有什么大问题:
private ODataQuery buildQuery(String path, String entity,
String sapClient, String sapLanguage) {
return ODataQueryBuilder
.withEntity(path, entity)
.withHeader("sap-client", sapClient, true)
.withHeader("sap-language", sapLanguage, true)
.withoutMetadata()
.build();
}
private ODataResponse executeQuery(ODataQuery query) {
return query.execute();
}
buildQuery 方法现在可以进行如下测试:
@Test
public void addsSapLanguageToHeader() throws ODataException, IOException {
ODataQuery query = cut.buildQuery("api/v2", "business-partners", "", "fr");
HttpUriRequest request = getRequest(query);
assertContainsHeader(request, "sap-language", "fr");
}
getRequest 方法会生成一个假的 HttpClient,它会存根开始工作所需的所有方法query.execute(httpClient)
。它存储实际请求并将其返回以供进一步检查。使用 Mockito 的示例实现:
private HttpUriRequest getRequest(ODataQuery query) throws ODataException, IOException {
// stub methods to make code work
HttpResponse response = mock(HttpResponse.class);
when(httpClient.execute(any())).thenReturn(response);
StatusLine statusLine = mock(StatusLine.class);
when(response.getStatusLine()).thenReturn(statusLine);
HttpEntity entity = mock(HttpEntity.class);
when(response.getEntity()).thenReturn(entity);
InputStream inputStream = new ByteArrayInputStream("".getBytes(StandardCharsets.UTF_8));
when(entity.getContent()).thenReturn(inputStream);
Header[] headers = new Header[0];
when(response.getAllHeaders()).thenReturn(headers);
// simulate the execution of the query
query.execute(httpClient);
// grab the original argument from the mock for inspection
ArgumentCaptor<HttpUriRequest> captor = ArgumentCaptor.forClass(HttpUriRequest.class);
verify(httpClient).execute(captor.capture());
HttpUriRequest request = captor.getValue();
return request;
}
当然,这个解决方案远非完美。
首先,单独完成这项工作所需的代码量表明,随着时间的推移,该测试将变得多么脆弱。每当 CloudSDK 决定向调用序列添加方法或验证时,此测试就会中断。另请注意,测试是侵入性的,通过测试一种private
方法,而黄金标准说我们应该只测试public
方法。
其次,executeQuery 方法仍然无法测试。执行路径也不同,因为测试代码使用.execute(httpClient)
变体运行查询,而原始代码使用.execute(destinationName)
变体。两者碰巧共享代码,但这可能会随着时间的推移而改变。