0

如何对这段代码进行单元测试?

private ODataQueryResult buildAndExecuteQuery(String path String entity,
                                 String sapClient, String sapLanguage) {
  ODataQuery query = ODataQueryBuilder
     .withEntity(path, entity)
     .withHeader("sap-client", sapClient, true)
     .withHeader("sap-language", sapLanguage, true)
     .withoutMetadata()
     .build();
  return query.execute();
}

更准确地说:如何验证我的代码是否调用了所有正确的函数,例如不会忘记调用withoutMetadata或设置所需的标头?

不幸的是,ODataQueryBuilderandODataQuery是类,而不是接口。这使嘲笑变得棘手。ODataQueryBuilder甚至final,完全禁用模拟。此外,该链以一个静态方法开始,该方法withEntity也不能被模拟。

是否有允许我监视行为或模拟数据的助手,类似于https://blogs.sap.com/2017/09/19/step-12-with-sap-s4hana-cloud-sdk-automatedMockUtil中描述的-测试/

4

2 回答 2

2

您是对的,这些类的结构使它们难以模拟,但由于它们是“用于服务开发的 SAP Cloud Platform SDK”的一部分,我们无法更改它们。

其他方法可能是:

  • 如果您想继续使用单元测试方法,您可能需要查看https://github.com/powermock/powermock。这将允许您模拟最终和静态类和方法。但是,我从未亲自使用过它,所以我不确定使用它是否容易/舒适。
  • 如果您还希望看到适合的集成测试,您可以考虑使用http://wiremock.org/docs/getting-started/。有了它,您将能够设置“模拟服务器”,为定义的请求准备响应,并验证您的测试进行的任何 HTTP 调用的内容。

    MockUtil我们在 SAP Cloud SDK 中使用 WireMock,并且还通过我们testutil-core模块中包含的内容提供了与我们的 SDK 的一些集成。

我希望这能有所帮助!

于 2019-09-14T13:01:30.643 回答
0

部分解决方案是在模拟 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)变体。两者碰巧共享代码,但这可能会随着时间的推移而改变。

于 2019-09-16T13:26:56.337 回答