Ohhnews

分类导航

$ cd ..
Baeldung原文

使用Microcks进行API模拟与测试

#microcks#api模拟#api测试#testcontainers#契约测试

[LOADING...]

1. 简介

在本教程中,我们将了解 Microcks——一个开源、Kubernetes 原生的平台,用于将 API 和微服务契约转换为实时的模拟接口。我们还将了解如何复用相同的契约,对真实实现运行一致性测试。

我们首先介绍核心概念,然后使用 Testcontainers 集成在一个 JUnit5 测试中启动 Microcks,导入 OpenAPI 契约,调用生成的模拟接口,并据此验证一个正在运行的服务。

2. 什么是 Microcks?

Microcks 是一个 CNCF(云原生计算基金会)项目,用于 API 模拟与测试。 它消费 API 和微服务产物,例如 OpenAPI 规范、AsyncAPI 规范、gRPC protobuf 文件、GraphQL 模式、Postman 集合和 SoapUI 项目,并能在数秒内将它们转换为可用的、有状态的模拟接口。

除了模拟之外,Microcks 还会复用这些相同的产物,对真实 API 实现运行契约一致性和非回归测试。 它还通过 CLI 与 Jenkins、GitHub Actions 和 Tekton 流水线集成,因此模拟和测试可以在整个交付链中实现自动化。

我们可以将 Microcks 作为独立服务器运行,使用 Docker Compose 或 Kubernetes/Helm 部署。不过,对于本地开发和 CI,使用 Testcontainers 模块将其直接嵌入到测试中会更方便。

Microcks 也不仅限于同步 REST API。通过其 ensemble 模式,它还可以模拟并对 SOAP、GraphQL、gRPC 服务,以及使用 AsyncAPI 描述、基于 Kafka、SQS 或 SNS 等代理的事件驱动 API 进行契约测试。这意味着同一个工具和同一套工作流可以覆盖典型微服务架构可能使用的大多数 API 风格。

3. 使用 Testcontainers 设置 Microcks

microcks-testcontainers 库可以让我们在测试类的生命周期内启动一个轻量级、用完即弃的 Microcks 实例。

首先,让我们添加 Maven 依赖:

$ xml
<dependency>
    <groupId>io.github.microcks</groupId>
    <artifactId>microcks-testcontainers</artifactId>
    <version>0.4.4</version>
</dependency>

接下来,让我们创建并启动一个 MicrocksContainer,并将它指向我们 API 的 OpenAPI 契约:

$ java
MicrocksContainer microcks = new MicrocksContainer(
  DockerImageName.parse("quay.io/microcks/microcks-uber:1.14.0"))
  .withMainArtifacts("apipastries-openapi.yaml");
microcks.start();

uber 镜像打包了 Microcks 所需的一切,无需外部 MongoDB 依赖,从而保持快速启动。我们也可以在容器启动后,通过 importAsMainArtifact()importAsSecondaryArtifact() 导入产物。

4. 模拟 API

一旦容器导入了契约,Microcks 会自动为每个操作暴露一个模拟端点。我们可以获取基础 URL,并像调用任何真实 API 一样调用它:

$ java
String baseApiUrl = microcks.getRestMockEndpoint("API Pastries", "0.0.1");
HttpResponse<String> response = client.send(
  HttpRequest.newBuilder(URI.create(baseApiUrl + "/pastries/Millefeuille")).GET().build(),
  HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());

Microcks 直接根据 OpenAPI 契约中定义的示例生成逼真的响应。 然后,它会循环使用这些示例,因此重复调用不会总是返回相同的负载。这意味着,即使某个依赖尚不存在,或者无法从测试环境访问,消费者也可以针对该依赖进行开发和测试。

此外,我们可以通过一些方式确认模拟接口确实被调用过。我们可以调用 microcks.verify("API Pastries","0.0.1"),或者通过 microcks.getServiceInvocationsCount("API Pastries","0.0.1") 检查确切的调用次数。

5. 运行契约测试

同一个契约可以验证运行在本机上的真实实现。首先,我们需要在启动容器之前(而不是之后)使用 Testcontainers.exposeHostPorts() 暴露端口。这样做是因为 Microcks 容器需要访问我们的宿主机:

$ java
Testcontainers.exposeHostPorts(port);
microcks.start();

然后,让我们针对本机运行的服务器发起一次一致性测试:

$ java
TestRequest testRequest = new TestRequest.Builder()
  .serviceId("API Pastries:0.0.1")
  .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
  .testEndpoint("http://host.testcontainers.internal:" + port)
  .filteredOperations(List.of("GET /pastries/{name}"))
  .timeout(Duration.ofSeconds(2))
  .build();
TestResult testResult = microcks.testEndpoint(testRequest);
assertTrue(testResult.isSuccess());

我们使用 filteredOperations() 将测试限定在单个操作上。如果不这样做,Microcks 还会重放契约中的 GET /pastriesPATCH /pastries/{name} 示例,而这些示例在尚未实现这些操作的实现上会失败。

在这里,Microcks 会将 OpenAPI 契约中的每个示例重放到我们正在运行的应用程序上,并根据 Schema 检查每个响应。 为了获得更友好的 JUnit 失败报告,我们可以改用 Assertions 辅助类,即 Assertions.assertSuccess(testResult);

这一 次调用所执行的一致性检查,与 CI 流水线对真实部署所执行的一致,只不过是在本地运行,且只需毫秒级时间。

6. 结论

在本文中,我们介绍了 Microcks 作为一个将 API 契约转换为实时模拟接口,并复用这些契约进行一致性测试的工具。我们使用它的 Testcontainers 集成在 JUnit 测试中启动了一个用完即弃的 Microcks 实例,导入 OpenAPI 契约,调用生成的模拟接口,并针对真实实现运行契约测试。

由于模拟接口和测试都源自同一个源契约,二者都能与 API 定义保持同步。 这消除了 API 承诺与实际行为之间常见的偏差来源。

和往常一样,完整代码可以在 GitHub 上获取。