简介
向微型服务的转变对所采用的测试战略产生了直接影响,并带来了一些需要解决的复杂问题。事实上,微服务需要额外的测试级别,因为我们必须处理多个独立的可部署组件。
Martin Fowler 在他的《微服务体系结构中的测试策略》演示文稿中对这些概念和各种级别的微服务测试给出了很好的解释。让我们来看看这个演示文稿中修改后的“测试金字塔”:
在本教程中,我们将深入研究组件测试: 在微服务体系结构中,组件就是服务本身。通过以这种粒度编写测试,API 的契约从使用者的角度通过测试来驱动。服务的隔离是通过使用测试双精度代替外部协作者和使用内部 API 端点来探测或配置服务来实现的[1]。
在下面的小节中,我们将介绍一个 SpringCloud 微服务示例的组件测试方法。
我们的示例 Spring Boot 微服务将具有以下特征:
我们将使用 Java 11、 Apache Maven 和 Docker 和一组协作库,这些库将使我们有可能在 CI/CD 管道中尽早单独测试服务,而不需要实际部署或启动其他服务、数据库,或占用完整测试环境的资源。
本演示的所有代码都发布在 GitHub 上。
我们的“订单跟踪”微服务由一个 Spring 控制器、服务和存储库组成:
Java 类 OrderControllerTest.java 将根据 API 提供的两个方法封装我们的组件测试。
现在,有很多方法可以使用 Maven 插件、 JUnit 特性、 Spring Boot 测试片段、命名约定,当然还有 CI 服务器上的正确脚本来分离和分类单元测试、集成测试、组件测试、契约测试等。通常情况下,并非所有的测试类别都在 CI/CD 流水线期间执行(或重新执行)。在本演示中,我们保持简单,但在真实场景中,强烈建议您实现适当的分类。
我们在/src/test/resources/application.yml 中的测试配置属性如下:
YAML
server:
port: 0
spring:
application:
name: order-service-test
cloud:
service-registry:
auto-registration:
enabled: false
loadbalancer:
ribbon:
enabled: false
config:
enabled: false
jpa:
show-sql: true
eureka:
client:
enabled: false
service-url:
registerWithEureka: false
okta:
oauth2:
issuer: https://kmandalas/oauth2/default
location-service:
url: http://localhost:9999/v1/track/在上面的文件中,注意我们已经禁用了 spring.cloud. config、 eureka.client 和 spring.cloud. service-Registry。自动登记。这是因为我们正在单独测试我们的微服务。因此,在启动时不会有 Spring Cloud Config 服务器为 OrderService 的配置属性提供服务,也不会有 Eureka 服务器注册并能够使用它来动态发现我们需要调用的 FulfulmentService 的服务。
当必须与数据库(关系数据库或 NoSQL)集成以进行测试时,理论上有三种选择:
有很多资源分析为什么选项一和选项二不是最好的,考虑到当然的测试阶段。例如,如果有人选择 H2进行集成和/或组件测试,那么他/她必须维护单独的 DDL 和 DML 脚本,因为产品 DB 很可能与 H2不同。而且,可能使用了本机查询或其他特定于 DB 的特性,使得这个选择很糟糕。另一方面,如果我们讨论的是端到端或性能测试等,那么应该使用实际部署的数据库,它将在测试环境中启动并运行。在这种情况下,现代 IaC (基础设施即代码)工具以及仔细的测试数据管理可以根据项目提供所需的灵活性。
对于我们特定的测试阶段,我们将使用选项3,利用 testContainer 和 Flyway,它们可以很好地与 Spring Boot 一起工作。数据库是 PostgreSQL。在 testContainer 的帮助下,将在测试上下文初始化的开始创建一个临时的 dockerized 数据库实例,Flyway 将在这个临时模式上触发我们的迁移脚本(DDL,dML)的执行。然后,我们的代码将透明地针对这个临时模式运行,当测试结束时,被修改的数据库将被释放。
在这里,我们所需要的只是 OrderControllerTest 类上的@TestContainer 标注以及以下静态声明:
@Container
static PostgreSQLContainer database = new PostgreSQLContainer("postgres:12")
.withDatabaseName("tutorial")
.withUsername("kmandalas")
.withPassword("dzone2022");
@DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry propertyRegistry) {
propertyRegistry.add("spring.datasource.url", database::getJdbcUrl);
propertyRegistry.add("spring.datasource.password", database::getPassword);
propertyRegistry.add("spring.datasource.username", database::getUsername);
}我们使用 Spring Cloud OpenFeign 来调用 FulfulmentService,它应该在我们的数据中心内,也就是另一个“内部”Spring Cloud 微服务,它应该在 Eureka 注册。在正常执行情况下,场景后面的假客户机通过名称定位目标服务实例,并执行客户端负载平衡(如果发现了多个实例)。
在我们的测试阶段,如果没有 Eureka (或者其他发现机制,比如 Consel) ,我们需要两样东西来尽可能真实地模拟这种集成:
作为一个嵌入式模拟服务器,我们可以使用 Hoverfly,但是我们更喜欢引入 WireMock,更具体地说是通过以下依赖关系:
XML
org.springframework.cloud
spring-cloud-starter-contract-stub-runner
test
这是因为使用 Spring-cloud-starter-contact-stub-runner,Spring Boot 应用程序测试套件中的 WireMock 的引导过程被简化了,而且它对于另一类非常重要的测试非常有用,即契约测试。有关更多信息,请查看 SpringCloud 契约 WireMock。
有了以上所有内容,我们需要做的就是用@AutoConfigureWireMock 注释我们的测试类,并在我们的测试资源目录下的 JSON 文件中定义一些 WireMock 映射。
对于这种集成,我们还将依赖于 WireMock。我们希望调用一个外部(第三方)服务,因此我们需要有效的 WireMock 映射(我们提供的响应越多,效果越好) ,当然还需要一个测试 URL,就像我们在测试资源应用程序中定义的那样。 yml:
YAML
location-service:br url: http://localhost:9999/v1/track/注意,这里我们提供了嵌入式服务器 WireMock 预期要运行的主机和端口,然后是外部服务的 URL 端点路径。端口不必硬编码,但可以定义为动态的。这样,如果我们在 CI/CD 流水线中并行运行多个组件测试,就不会出现端口冲突。
还有一点需要注意的是,WireMock 不仅可以用来模拟 RESTful 服务的 JSON 响应,还可以模拟基于 SOAP 的 Web 服务的响应!
正如我们上面提到的,SpringCloud 微服务基础设施通常会合并一个 API 网关,比如 SpringCloudGateway。这为处理安全性提供了各种选项。我们将使用 OAuth 2.0、 JavaScript 对象签名和加密(JOSE)以及 JSON Web Tokens 标准所支持的令牌中继模式。这将使我们的用户能够识别他们自己,授权应用程序查看他们的配置文件,并访问网关后面的安全资源。在这种情况下,一个非常常见的设置包括以下组件:
因为在这个测试阶段,我们将单独测试 Spring Boot 微服务,所以我们将使用 Spring Security 的 SecurityMockMvcRequestPostProcessor。这将使我们能够在 MockMvc 调用期间传递有效的 JWT 和定义权限(即用户角色) ,并在启用安全性的情况下测试组件的行为。例如:
mockMvc.perform(get("/api/orders/11212/status").with(jwt())).andExpect(status().isOk());还有
mockMvc.perform(get("/api/orders/").with(jwt().authorities(new SimpleGrantedAuthority("backoffice"))))
.andExpect(status().isOk());在 CI/CD 行话中有一句流行的话说..。
The secret to success is to... "fail fast!"
成功的秘诀就是... “快速失败!”
因此,对于成功的交付来说,包含各种类型的测试的均衡组合是至关重要的,这些测试将由开发人员执行,并且在 CI/CD 流水线期间以自动方式执行。在本教程中,我们重点介绍了 Spring Cloud 微服务组件测试,并提供了一些常见用例的配方。测试愉快!
| 留言与评论(共有 0 条评论) “” |