无论您是遵循传统的测试金字塔还是采用诸如“测试蜂窝”这样的较新方法,都应该在开发过程中的某个时候开始编写集成测试用例。

您可以编写不同类型的集成测试。从持久性测试开始,您可以检查组件之间的交互,也可以模拟调用外部服务。本文将讨论后一种情况。
在谈论WireMock之前,让我们从一个典型的例子开始。

ChuckNorrisService

我们有一个简单的API,用于手动测试。在“业务”类意外是,它可以调用外部API。它使用Spring 框架提供功能的。没什么特别的。我多次看到的是模拟RestTemplate并返回一些预先确定的答案的测试。该实现可能如下所示:

@Service public class ChuckNorrisService{ ...   public ChuckNorrisFact retrieveFact() {     ResponseEntity<ChuckNorrisFactResponse> response = restTemplate.getForEntity(url, ChuckNorrisFactResponse.class);     return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT);   }  ...  }

在检查成功案例的常规单元测试旁边,将至少有一项覆盖HTTP错误码的测试用例,即4xx或5xx状态代码:

@Test   public void shouldReturnBackupFactInCaseOfError() {     String url = "http://localhost:8080";     RestTemplate mockTemplate = mock(RestTemplate.class);     ResponseEntity<ChuckNorrisFactResponse> responseEntity = new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE);     when(mockTemplate.getForEntity(url, ChuckNorrisFactResponse.class)).thenReturn(responseEntity);     ChuckNorrisService service = new ChuckNorrisService(mockTemplate, url);     ChuckNorrisFact retrieved = service.retrieveFact();     assertThat(retrieved).isEqualTo(ChuckNorrisService.BACKUP_FACT);   }

看起来还不错吧?响应实体返回503错误代码,我们的服务不会崩溃。所有测试都是绿色通过的,我们可以部署我们的应用程序。
不幸的是,Spring的RestTemplate不能这样使用。方法签名getForEntity给了我们很小的提示。它指出throws RestClientException。这就是mock的RestTemplate与实际实现不同的地方。我们将永远不会收到ResponseEntity带有4xx或5xx状态代码的。RestTemplate将抛出的子类RestClientException。通过查看类的层次结构,我们可以对可能抛出的结果有一个很好的印象:

因此,让我们看看如何使这项测试更好。

WireMock进行拯救

WireMock通过启动模拟服务器并返回将其配置为返回的答案来模拟Web服务。得益于出色的DSL,它很容易集成到您的测试中,并且模拟请求也很简单。

对于JUnit 4,有一个WireMockRule有助于启动停止服务器的工具。对于JUnit 5,大概需要自己做一个这样的工具。当您检查示例项目时,您可以找到ChuckNorrisServiceIntegrationTest。这是基于JUnit 4的SpringBoot测试。让我们看一下。

最重要的部分是ClassRule:

@ClassRule   public static WireMockRule wireMockRule = new WireMockRule();

如前所述,这将启动和停止WireMock服务器。您也可以像往常一样使用该规则Rule来启动和停止每个测试的服务器。对于我们的测试,这不是必需的。

接下来,您将看到几种configureWireMockFor...方法。这些包含WireMock何时返回答案的说明。将WireMock配置分为几种方法并从测试中调用它们是我使用WireMock的方法。当然,您可以在一个@Before方法中设置所有可能的请求。对于正确使用的Demo,我们这样做:

public void configureWireMockForOkResponse(ChuckNorrisFact fact) throws JsonProcessingException {     ChuckNorrisFactResponse chuckNorrisFactResponse = new ChuckNorrisFactResponse("success", fact);     stubFor(get(urlEqualTo("/jokes/random"))         .willReturn(okJson(OBJECT_MAPPER.writeValueAsString(chuckNorrisFactResponse))));   }

所有方法都是从静态导入的com.github.tomakehurst.wiremock.client.WireMock。如您所见,我们将HTTP GET存入路径/jokes/random并返回JSON对象。该okJson()方法只是带有JSON内容的200响应的简写。对于错误情况,代码甚至更简单:

private void configureWireMockForErrorResponse() {     stubFor(get(urlEqualTo("/jokes/random"))         .willReturn(serverError()));   }

如您所见,DSL使阅读说明变得容易。将WireMock放置在适当的位置,我们可以看到我们先前的实现不起作用,因为RestTemplate引发了异常。因此,我们必须调整代码:

public ChuckNorrisFact retrieveFact() {     try {       ResponseEntity<ChuckNorrisFactResponse> response = restTemplate.getForEntity(url, ChuckNorrisFactResponse.class);       return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT);     } catch (HttpStatusCodeException e){       return BACKUP_FACT;     }   } 

这已经涵盖了WireMock的基本用例。配置请求的答案,执行测试,检查结果,so easy,就这么简单。尽管如此,在云环境中运行测试时通常会遇到