《微服务架构设计模式》读书笔记 | 第10章 微服务架构中的测试策略(下)
作者:互联网
目录
新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!
前言
本章介绍集成测试、组件测试与端到端测试;
这是一本关于微服务架构设计方面的书,这是本人阅读的学习笔记。以下对一些符号做些说明:
()为补充,一般是书本里的内容;
[]符号为笔者笔注;
1. 编写集成测试
集成测试验证服务是否可以与其客户端和依赖关系进行通信;
1.1 服务通常与其他服务交互
测试服务间的交互方法:
- 端到端测试:启动所有服务并通过其API进行测试;
- 集成测试:不启动服务,可以通过一些策略简化测试;
1.2 集成测试的策略
集成测试的两种策略:
- 第一种:测试每个服务的适配器,以及可能的适配器支持类,如JPA持久化测试;
- 第二种:使用契约,契约是一对服务之间交互的具体实例;契约的结构取决于服务之间的交互类型;
- 实现契约的方法取决于测试消费者还是提供者:
- 消费者端测试:用于消费者适配器的测试。它们使用契约来配置桩,以此模拟提供者程序的行为,使你能够直接运行测试,而不需要运行消费者对应的提供者程序;
- 提供者端测试:用于提供者适配器的测试。它们使用契约来测试适配器,使用模拟来满足适配器的依赖关系;
1.3 针对持久化层的集成测试
持久化集成测试每个阶段的行为:
- 设置:通过创建数据库结构设置数据库,并将其初始化为已知状态。也可能开始执行一些必要的数据库事务;
- 执行:执行数据库操作;
- 验证:对数据库的状态和从数据库中检索的对象进行断言;
- 验证:对数据库的状态和从数据库中检索的对象进行断言;
- 拆解:可选阶段,可以撤销对数据库所做的更改,如回滚在设置阶段提交的事务;
1.4 针对基于REST的请求 / 响应式交互的集成测试
图解:
- 消费者端:消费者端的API Gateway集成测试会使用契约来配置一个模拟Order Service行为的HTTP桩服务器;
- 图示的WireMock是一个有效模拟HTTP服务器的工具;
- 提供者端:Spring Cloud Contract 使用契约来生成提供者端Order Service集成测试代码,该测试使用Spring Cloud MVC 或 Rest Assured Mock MVC测试控制器;
- 这里的测试代码在HttpTest测试类中;
- 契约:契约中的请求内容指定了从API Gateway发出的HTTP请求;契约的响应指定了桩向API Gateway发送回的响应;
- 该契约描述了API Gateway成功从Order Service检索Order的尝试;
- 该测试将验证Order Service的API是否满足消费者的期望;
- 该测试验证是否符合契约;
1.5 针对发布 / 订阅式交互的集成测试
![使用Spring Cloud Contract来测试基于领域事件的交互
图解:
- 消费者端:每个消费者端测试都会发布契约指定的事件,并验证OrderHistoryEventHandlers是否正确调用了其模拟的依赖项;
- 提供者端:提供者端验证是否发布确认契约的事件;
- 在这里,Spring Cloud Contract继承自MessagingBase的测试类;
- 每个测试方法调用MessagingBase定义的hook方法触发服务发布事件;
- 契约:这里的交互由一组契约定义,不同于REST契约,每个交互的契约都指定了一个领域事件;
- OrderCreated事件的契约指定时间的通道,以及预期的消息主体和头部;
- 契约中有另外两个重要元素:
- label:用于消费者测试,触发Spring Contact发布事件;
- triggeredBy:生成的测试方法调用的超类方法的名称,用于触发事件的发布;
- 其验证负责发布Order聚合领域事件的OrderDomainEventPublisher是否发布了符合客户期望的事件;
- OrderHistoryEventHandlers类的事件处理程序调用OrderHistoryDao来更新CQRS视图;
- 每个测试方法首先调用Spring Cloud来发布契约中定义的事件,然后验证OrderHistoryEventHandlers是否正确调用了OrderHistoryDao;
1.6 针对异步请求 / 响应式交互的集成契约测试
图解:
- 消费者端:消费者端测试验证命令消息代理类是否发送了结构正确的命令消息,并正确处理回复消息;
- 在本例中,KitchenServiceProxyTest测试KitchenServiceProxy;
- 其使用Spring Cloud Contract配置消息桩,验证命令消息是否与契约的输入消息匹配,并使用相应的输出消息进行回复;
- 提供者端:提供者端测试由Spring Cloud Contract代码生成。每种测试方法对应一份契约;
- 提供者端测试方法将契约的输入消息作为命令消息发送,并验证回复消息是否与契约的输出消息匹配;
- 契约:需要就命令式消息通道的名称以及命令和回复消息的结构达成一致;与REST交互方式不同的是,命令的契约指定输入消息和输出消息,而不是HTTP请求和回复;
- 此交互契约由输入消息和输出消息组成;
- 这两条消息都指定了消息通道、消息主体和消息头部;命名约定来自提供者的角度;
- 输入消息是发送到KitchenService通道的CreateTicket命令;输入消息的messageFrom元素指定从中读取消息的通道;
- 输出消息是一个成功的回复,发送到CreateOrderSaga的回复通道;输出消息的sentTo元素指定应该将回复发送到的通道;
- 该测试调用服务的消息代理,并从两个方面验证其行为;
- 首先验证消息代理发送的是否符合契约的命令消息;
- 其次验证代理是否正确地处理回复消息;
- 提供者端集成测试必须通过发送回复来验证提供者是否正确处理了命令消息;
- Spring Cloud Contract生成的测试类具有针对每个契约的测试方法;
- 每个测试方法都会发送契约的输入消息,并验证回复是否与契约的输出消息相匹配;
2. 编写组件测试
服务组件测试模式:单独测试服务;
组件测试单独验证服务的行为,使用模拟其行为的桩代替服务的依赖关系;
2.1 定义验收测试
- 验收测试是针对软件组件的面向业务的测试;
- 验收测试从组件客户端而不是内部实现的角度描述了所需的外部可见行为;
- 测试源自用户故事或用例;
2.2 使用Gherkin编写验收测试
使用Java编写验收测试具有挑战性,更好的方法是消除手动转换步骤并编写可执行的场景;
Gherkin用于编写可执行规范的DSL;使用Gherkin可以使用类似英语场景定义验收测试,然后使用Cucumber执行规范;
2.2.1 使用Gherkin定义验收测试
图解:
- Gherkin规范提供一系列功能,每个功能都有一组场景描述,场景具有given-when-then结构:
- given:先决条件;
- when:发生的动作或事件;
- then / and:预期的结果;
- 图示代码是Place Order功能的部分代码,功能包括几个元素:
- 名称:对于此功能,名称为Place Order;
- 规范简介:描述该功能存在的原因。对于此功能,规范简介是用户故事;
- 场景:Order authorized 和 Order rejected due to expired credit card;
- 用户在第一个场景成功;第二个失败,因为信用卡过期;
2.2.2 使用Cucumber执行规范
图解:
- Cucumber是一个自动化框架,用于执行Gherkin编写的测试场景;其有多种语言实现方式;
- 使用 Cucumber for Java 时,可以编写步骤自定义类,如上述代码所示;
- 注解的值元素是一个正则表达式,Cucumber与步骤匹配;
- @Given:设置阶段;
- @When:执行阶段;
- @Then 和 @And:验证阶段;
2.3 设计组件测试
为了避免为多个服务配置桩,设置数据库和消息传递基础设施;
- 进程内组件测试:
- 进程内组件测试使用常驻内存的桩和模拟代替其依赖性来运行服务;
- 如:使用@SpringBootTest来为Spring Boot应用测试;
- 好处是编写简单,速度快;缺点是不测试服务的可部署性;
- 进程外组件测试:
- 进程外组件测试使用真实的基础设施服务,但对应用程序服务的任何依赖项使用桩;
- 好处是提高测试覆盖率;缺点是编写复杂、执行速度慢;可能比进程内组件测试脆弱;
- 如何为进程外组件测试编写桩服务:
- 被测服务通常使用涉及发回响应的交互方式来调用依赖项;
- 进程外测试必须为这些类型的依赖项配置桩,这些依赖项处理请求并发回回复;
- 可以使用Spring Cloud Contract;更简单的选择是在测试内部部署桩;
2.4 为FTGO的Order Service编写组件测试
Order Service的组件测试,使用进程外策略来测试作为Docker容器运行的服务;
2.4.1 Order Service的组件测试设计
图解:
- 几个注解的含义:
- @CucumberOptions:指定在哪可以找到Gherkin功能文件;
- @RunWith(Cucumber.class):告诉JUNIT使用Cucumber测试运行器;
- @RunWith注解通过读取Gherkin功能定义测试,并使用OrderServiceComponentTestStepDefinitions类使它们可执行;
- OrderServiceComponentTestStepDefinitions类是测试核心,它使用@ContextConfiguration注解,创建了Spring ApplicationContext;
- Spring ApplicationContext定义了各种Spring组件,包括消息桩等;
2.4.2 OrderServiceComponentTestStepDefinitions类
- 此方法使用SagaParticipantStubManager测试辅助类,为Saga参与方构造桩;
- 此方法定义了
When I place an order for Chicken Vindaloo at Ajanta
这一步骤;
- 该方法定义
Then the order should be ...
步骤的含义,验证Order是否已成功创建并且处于预期状态;
- 该方法定义了
And an event should be published
步骤,验证预期的领域事件是否已发布;
2.4.3 运行组件测试
图解:
- 测试使用Docker来运行Order Service及其依赖项;
- 前面Gradle做了两件事:
- 将Gradle Docker Compose插件配置为在组件测试之前运行,并启动Order Service及其它配置为依赖的基础设施服务;
- 将componentTest配置为依赖于assemble,以便首先构建Docker映像所需的JAR文件;
- 做完两件事后,用
./gradlew :ftgo-order-service:componentTest
命令运行这些组件测试;该命令做了以下操作:- 构建Order Service;
- 运行服务及其基础设施服务;
- 运行测试;
- 停止正在运行的服务;
3. 端到端测试
端到端测试位于测试金字塔顶端,会测试整个应用程序;
- 端到端测试的缺点:开发缓慢、脆弱且耗时;
3.1 设计端到端测试
- 需要控制端到端测试数量;
- 可以使用用户旅程测试策略,将几个关联的测试编写成一个单一测试;
3.2 编写端到端测试
图解:
- 面向业务的测试,写在业务人员理解的DSL中;
- 与验收测试的区别在于,有多个动作而不是单一的Then;
3.3 运行端到端测试
- 端到端测试必须运行整个应用程序,包括任何所需的基础设施服务;
- 可以使用Gradle Docker Compose插件运行所有应用程序的服务;
4. 本章小结
- 使用契约作为示例消息来驱动服务之间交互的测试。编写测试以验证两个服务的适配器是否符合契约,而不是编写运行两个服务及其传递依赖关系的慢速测试;
- 编写组件测试以通过其API验证服务的行为。应该通过单独测试服务来简化和加速组件测试,使用桩来解决其依赖项;
- 编写用户旅程测试,以最大限度地减少端到端测试的数量,这些测试缓慢、脆弱又耗时。用户旅程测试模拟用户在应用程序中的旅程,并验证相对较大的应用程序功能片段的高级行为。因为测试很少,所以每次测试开销的数量(如测试设置)被最小化,这就加快了测试速度;
最后
标签:架构设计,服务,10,读书笔记,验证,测试,契约,组件,Order 来源: https://www.cnblogs.com/dlhjw/p/15348167.html