软件开发教父--MartinFowler在其名为《??微服务架构的测试策略??》的演讲中详细阐述了测试不同层次微服务的概念,其中提到了“测试金字塔”模型。该模型从下到上依次为:单元、集成、组件、端到端和探索。不可否认,随着云微服务在业界的广泛采用,我们需要提高微服务应用的测试水平,并根据需要增加测试策略的复杂度,同时受益于处理多个可独立部署的组件。接下来,我将从用户的角度,以一个SpringCloud微服务为例,深入探讨各个服务组件的相关测试。服务我们的SpringBoot微服务示例将具有以下特征:将启用SpringCloudNetflix。它将使用NetflixOSS集成SpringBoot应用程序来执行服务注册和发现、分布式外部配置(SpringCloudConfig)和客户端负载平衡。它将与关系数据库(如PostgreSQL)集成并将能够调用另一个(内部)微服务将调用第三方(外部)Web服务将启用SpringSecurity充当OAuth2资源服务器将被“隐藏”在API网关服务器(例如SpringCloudGateway)之后,我们将通过Java11、ApacheMaven、Docker和一组协作库,在CI/CD管道的“早期”进行单独的服务测试,而无需实际部署或绑定其他服务、数据库,甚至完整的测试环境资源。同时,您可以通过链接-https://github.com/kmandalas/spring-cloud-component-tests在GitHub上获取本示例的所有代码。本示例中的“订单跟踪”微服务由Spring控制器、服务和存储库组成。它暴露了两个端点:GET/api/orders/{trackingNumber}/status:它通过给定的跟踪号执行数据库查询以获取相关订单;然后调用FulfillmentService的内部服务来判断配送状态;并让最终的外部服务调用定位服务,实现根据状态定位。这是具有有效JWT的受保护API调用。GET/api/orders:通过查询数据库列出所有订单。这是受额外授权限制的受保护API调用。它仅适用于具有“后台”角色的用户。组件测试OrderControllerTest.java类将封装API提供的各种方法的组件测试。比如我们可以选择包括:Maven插件、JUnit函数、SpringBoot测试分片和分类单元测试、集成测试、组件测试、契约测试等方式。当然,并非所有测试类别都需要在CI/CD管道中执行(或重新执行)。鉴于此示例的简单性,我强烈建议您实施适当的分类。在/src/test/resources/application.yml中,我们对属性的测试配置如下:YAMLserver:port:0spring:application:name:order-service-testcloud:service-registry:auto-registration:enabled:falseloadbalancer:ribbon:启用:false配置:启用:falsejpa:show-sql:trueeureka:客户端:启用:falseservice-url:registerWithEureka:falseokta:oauth2:发行者:https://kmandalas/oauth2/defaultlocation-service:url:http://localhost:9999/v1/track/在上面的代码片段中,我们禁用了spring.cloud.config、eureka.client和spring.cloud.service-registry.auto-registration因为,这样方便测试微服务隔离。因此,SpringCloudConfig服务器在启动时都不会为OrderService的配置属性提供服务;Eureka服务器也不会提供注册,并能够使用它来按需调用FulfillmentService的动态服务发现。数据库当出于测试目的需要与数据库(关系型或NoSQL)集成时,我们通常有三种选择:使用嵌入式或内存中的解决方案,例如:H2,https://www.h2database.com/使用在测试期间可以访问的真实数据库使用与生产数据库接近甚至相同的暂存数据库不同的选项将涉及不同的测试资源。如果您采用第一种方法,H2用于集成和组件测试,那么您将不得不维护各种单独的DDL和DML脚本,因为生产环境的数据库很可能与H2不同。此外,您还可以使用本机查询或特定数据库特定的其他功能。如果您需要进行端到端或性能测试,那么您应该部署一个真实的数据库并在测试环境中启动并运行它。在这方面,现代IaC(infrastructureascode,基础设施即代码)工具和详细的测试数据管理将为按需项目提供灵活性。在这个测试例子中,我们将采用第三种方式,使用testcontainers和Flyway,实现与SpringBoot的配合,而数据库使用PostgreSQL。在测试容器的帮助下,我们将在测试的初始化阶段创建一个临时的dockerized数据库实例。而Flyway会在这个临时模式(schema)上触发迁移脚本(DDL/DML),这样我们的代码就会透明地运行在这个临时模式上。当测试完成后,我们将处理dockerized数据库。可以看出,我们实际上只需要OrderControllerTest类上的@Testcontainers注解,以及下面的静态声明:kmandalas”)propertyRegistry.add("spring.datasource.password",database::getPassword);propertyRegistry.add("spring.datasource.username",database::getUsername);}内部服务调用我们将使用SpringCloudOpenFeign来调用FulfillmentService,这是另一个可以注册到Eureka的“内部”SpringCloud微服务。在正常执行的情况下,后台的feignclient可以通过名称定位到目标服务实例,实现客户端负载均衡(如果发现多个实例)。在我们的测试中,在没有Eureka(或Consul等其他发现机制)的情况下,我们需要通过以下两个方面尽可能真实地模拟这种集成:模拟服务器。服务器可以根据URL的不同模式拦截请求,并使用我们提供的模拟响应进行回复。使用@TestConfiguration模拟各种FulfillmentService实例的发现,并将它们指向WireMock服务器的URI。你可以通过链接查看这个类--https://github.com/kmandalas/spring-cloud-component-tests/blob/50241126932fce3e9cfc6351291af5857f77806a/src/test/java/gr/kmandalas/dzone/OrderControllerTest.java#L55Test配置。当然,你也可以使用Hoverfly作为嵌入式模拟服务器。本例中,我们通过如下依赖设置引入WireMock:test通过spring-cloud-starter-contract-stub-runner,简化了SpringBoot应用测试套件中WireMock的引导,对于契约测试非常有用(contracttests)也很有用。请参阅SpringCloudContractWireMock的链接-https://docs.spring.io/spring-cloud-contract/docs/current/reference/html/project-features.html#features-wiremock了解更多信息。有了以上基础,我们只需要使用@AutoConfigureWireMock注解测试类,在测试资源目录下的JSON文件中定义各种WireMock映射即可。外部服务调用在集成的过程中,为了能够调用一些外部(第三方)服务,我们还是需要依赖有效的WireMock映射(毕竟我们能提供的响应越多越好),所以它们可以在application.yml测试URL资源中定义。这是一个简单的示例:YAMLlocation-service:url:http://localhost:9999/v1/track/我们在外部服务的URL端点路径中提供运行WireMock嵌入式服务器的主机和端口。端口号虽然不一定是硬编码的,但可以定义为动态的,以便在CI/CD管道中并行运行多个组件测试而不会发生端口冲突。值得一提的是,WireMock不仅可以用来模拟来自RESTful服务的各种JSON响应,还可以用来模拟来自基于SOAP的Web服务的响应。安全如前所述,SpringCloud微服务基础设施通常可以包含一个API网关,例如SpringCloudGateway。相应地,我们可以使用OAuth2.0、JavaScriptObjectSigningandEncryption(JOSE)和JSONWebToken标准的令牌中继模式来处理用户身份识别和授权应用程序查看其个人数据,以及访问网关后的安全资源。通常这样的安全设置将包括一个单点登录服务器,例如Keycloak、CloudFoundry的用户帐户和身份验证服务器,以及像Okta这样的商用OAuth2身份验证提供程序。API网关服务器,例如SpringCloudGateway,将用户账户的管理和授权委托给单点登录服务器。资源服务器:本SpringBoot微服务示例中的OrderService。对于这个测试示例,我们将在单独测试SpringBoot微服务时使用SpringSecurity的SecurityMockMvcRequestPostProcessors。它将允许我们在MockMvc调用期间传递有效的JWT,定义权限(即用户角色),并在启用安全性的情况下测试组件的行为。例如:JavamockMvc.perform(get("/api/orders/11212/status").with(jwt())).andExpect(status().isOk())和mockMvc.perform(get("/api/命令/").with(jwt().authorities(newSimpleGrantedAuthority("backoffice")))).andExpect(status().isOk());总结如今,为了成功交付产品,开发人员能够在CI/CD管道中,以自动化方式执行各种测试至关重要。希望以上讨论的SpringCloud微服务组件测试的相关指南和注意事项,能够在您实际的项目交付中有所帮助。译者简介JulianChen,社区编辑,拥有十余年IT项目实施经验,擅长管控内外部资源和风险,专注传播网络与信息安全方面的知识和经验;以多种形式分享前沿技术和新知识;经常在线上和线下开展信息安全培训和讲座。原标题:SpringCloudMicroservices的组件测试,作者KyriakosMandalas和DimitrisStavroulakis
