网站首页 > 教程文章 正文
在本教程中,我们讨论了 Spring Cloud 微服务组件测试的指南和注意事项,并提供了常见用例的处方
简介
向微型服务的转变对所采用的测试战略产生了直接影响,并带来了一些需要解决的复杂问题。事实上,微服务需要额外的测试级别,因为我们必须处理多个独立的可部署组件。
Martin Fowler 在他的《微服务体系结构中的测试策略》演示文稿中对这些概念和各种级别的微服务测试给出了很好的解释。让我们来看看这个演示文稿中修改后的“测试金字塔”:
在本教程中,我们将深入研究组件测试: 在微服务体系结构中,组件就是服务本身。通过以这种粒度编写测试,API 的契约从使用者的角度通过测试来驱动。服务的隔离是通过使用测试双精度代替外部协作者和使用内部 API 端点来探测或配置服务来实现的[1]。
在下面的小节中,我们将介绍一个 SpringCloud 微服务示例的组件测试方法。
服务
我们的示例 Spring Boot 微服务将具有以下特征:
- Spring Cloud Netflix 将启用,也就是说,它将使用针对 Spring Boot 应用的 Netflix OSS 集成来执行服务注册和发现、分布式/外部配置(Spring Cloud Config)和客户端负载平衡
- 将与一个关系数据库(postgreSQL)集成
- 将执行对另一个微服务(内部)的调用
- 将执行对第三方 Web 服务(外部)的调用
- 将启用 Spring Security (将充当 OAuth2资源服务器)
- 将“隐藏”在 API 网关服务器之后,如Spring Cloud Gateway 春云之门
我们将使用 Java 11、 Apache Maven 和 Docker 和一组协作库,这些库将使我们有可能在 CI/CD 管道中尽早单独测试服务,而不需要实际部署或启动其他服务、数据库,或占用完整测试环境的资源。
本演示的所有代码都发布在 GitHub 上。
我们的“订单跟踪”微服务由一个 Spring 控制器、服务和存储库组成:
- GET /api/orders/{trackingNumber}/status GET/api/orders/{ trackingNumber }/status: 给定一个跟踪号码,执行一个 DB 查询以获取相关订单,然后执行对FulfillmentService 实践服务以确定交付状态,并根据状态,对LocationService 职位服务, 这是一个受保护的 API 调用,必须有一个有效的 JWT
- GET /api/orders 获取/应用程序接口/订单: 通过查询数据库列出所有订单。它也是一个受保护的 API 调用,这一次带有额外的授权限制ーー它只对具有“后台办公室”角色的用户可用
组件测试
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 for example) 例如)
- 使用测试期间可访问的实际数据库
- 使用与生产数据库相近甚至相同的临时数据库
有很多资源分析为什么选项一和选项二不是最好的,考虑到当然的测试阶段。例如,如果有人选择 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);
}
The Internal Service Call
我们使用 Spring Cloud OpenFeign 来调用 FulfulmentService,它应该在我们的数据中心内,也就是另一个“内部”Spring Cloud 微服务,它应该在 Eureka 注册。在正常执行情况下,场景后面的假客户机通过名称定位目标服务实例,并执行客户端负载平衡(如果发现了多个实例)。
在我们的测试阶段,如果没有 Eureka (或者其他发现机制,比如 Consel) ,我们需要两样东西来尽可能真实地模拟这种集成:
- 用于启动一个模拟服务器,该服务器根据 URL 模式拦截我们的请求,并使用我们提供的模拟响应进行响应
- A @TestConfiguration t将会模仿发现FulfillmentService 实践服务你可以看看这个测试配置here 给你.
作为一个嵌入式模拟服务器,我们可以使用 Hoverfly,但是我们更喜欢引入 WireMock,更具体地说是通过以下依赖关系:
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
<scope>test</scope>
</dependency>
这是因为使用
Spring-cloud-starter-contact-stub-runner,Spring Boot 应用程序测试套件中的 WireMock 的引导过程被简化了,而且它对于另一类非常重要的测试非常有用,即契约测试。有关更多信息,请查看 SpringCloud 契约 WireMock。
有了以上所有内容,我们需要做的就是用@AutoConfigureWireMock 注释我们的测试类,并在我们的测试资源目录下的 JSON 文件中定义一些 WireMock 映射。
The External Service Call
对于这种集成,我们还将依赖于 WireMock。我们希望调用一个外部(第三方)服务,因此我们需要有效的 WireMock 映射(我们提供的响应越多,效果越好) ,当然还需要一个测试 URL,就像我们在测试资源应用程序中定义的那样。 yml:
YAML
location-service:br url: http://localhost:9999/v1/track/
注意,这里我们提供了嵌入式服务器 WireMock 预期要运行的主机和端口,然后是外部服务的 URL 端点路径。端口不必硬编码,但可以定义为动态的。这样,如果我们在 CI/CD 流水线中并行运行多个组件测试,就不会出现端口冲突。
还有一点需要注意的是,WireMock 不仅可以用来模拟 RESTful 服务的 JSON 响应,还可以模拟基于 SOAP 的 Web 服务的响应!
The Security
正如我们上面提到的,SpringCloud 微服务基础设施通常会合并一个 API 网关,比如 SpringCloudGateway。这为处理安全性提供了各种选项。我们将使用 OAuth 2.0、 JavaScript 对象签名和加密(JOSE)以及 JSON Web Tokens 标准所支持的令牌中继模式。这将使我们的用户能够识别他们自己,授权应用程序查看他们的配置文件,并访问网关后面的安全资源。在这种情况下,一个非常常见的设置包括以下组件:
- 单一登录服务器,比如Keycloak Cloud Foundry’s User Account and Authentication Server 或者VS commercial OAuth2 authentication providers 如Okta
- 网关服务器,例如将用户帐户管理和授权委托给单点登录服务器的 Spring Cloud Gateway
- 资源服务器: 我们的 SpringBoot 微服务,如OrderService 订单服务 在我们的例子中
因为在这个测试阶段,我们将单独测试 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 微服务组件测试,并提供了一些常见用例的配方。测试愉快!
猜你喜欢
- 2025-05-10 金仓数据库日志大揭秘:WalMiner工具实战全解析
- 2025-05-10 十年之重修MySQL原理(十年之重修mysql原理是什么)
- 2025-05-10 值得收藏的Oracle数据库性能优化(oraclesql性能优化)
- 2025-05-10 MySQL锁机制:从表锁到MVCC,一场数据库的“锁”事大戏
- 2025-05-10 MySQL日志篇(mysql日志详解)
- 2025-05-10 十个你必须会的mysql面试题(mysql面试题经典)
- 2025-05-10 利用Oracle触发器实现不同数据库之间的数据同步
- 2025-05-10 GaussDB关键技术原理|高可用:逻辑复制
- 2025-05-10 一文了解MySQL Binlog(一文了解太空安全有多重要)
- 2025-05-10 SQL审核平台——Yearning(sql审核工具)
- 05-11阿里开源MySQL中间件Canal快速入门
- 05-11MyBatis插件开发实战:手写一个分页插件
- 05-11Flask数据库——SQLAlchemy
- 05-11MySQL 到 Hazelcast Cloud 实时数据同步实操分享
- 05-11sqlmap 详解
- 05-11一篇文章让你学会Elasticsearch中的查询
- 05-11Mysql性能优化这5点你知道吗?简单却容易被初学者忽略!
- 05-11Spring Boot 实现 MySQL 读写分离技术
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- md5 sha1 (32)
- mybatis plus page (35)
- semaphore 使用详解 (32)
- update from 语句 (32)
- vue @scroll (38)
- 堆栈区别 (33)
- 在线子域名爆破 (32)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)