程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

《微服务架构设计模式》读书笔记(九):测试策略(上)

balukai 2025-04-06 13:37:26 文章精选 9 ℃

导读

  • 微服务中有效的测试策略
  • 使用模拟(mock)和桩(stub)对软件中的元素执行隔离测试
  • 使用测试金字塔确定测试工作的重点
  • 对服务中的类执行单元测试

传统测试在微服务架构中有两大缺点:

  • 手动测试效率极低
  • 在交付流程中才进行测试为时已晚

自动化测试

2018年Saucc Labs测试趋势报告显示,自动化测试的应用情况不容乐观,大约26%的公司的大部分测试是自动化的,而只有3%的公司的测试是完全自动化的,缺乏自动测试的文化是主因。

使用微服务架构的一个关键动机是提高可测试性,与此同时,微服务架构的复杂性要求编写自动化测试,这是缩短交付周期(即代码投入生产环境)的唯一方法。

微服务架构中的测试策略

手动测试需要运行素有依赖项,包括基础设施服务,费时费力,更好的选择是在开发过程中就引入自动化测试。

你的开发工作流程应该是:编写代码、运行测试,然后重复这两件事情。

但如何编写可以快速运行的测试?它们是否足够,是否还需要更全面的测试?

本章将尝试回答这些问题。

编写自动化测试

测试用例是用于特定目标的一组测试输入、执行条件和预期结果,例如执行特定程序路径或验证是否满足特定要求。

自动化测试通常使用测试框架编写,例如JUnit。

自动化测试通常包括四个阶段:设置环境、执行测试、验证结果、清理环境。

使用模拟进行测试

被测系统在运行时常依赖其他系统,依赖会将测试复杂化,并减慢测试的速度,可以使用测试替身来消除被测系统的依赖性。

测试替身是一个对象,该对象负责模拟依赖项的行为,有两种类型的测试替身:

  • 桩(stub):它代表依赖项来向被测系统发送调用的返回值
  • 模拟(mock):用来验证被测系统是否正确调用依赖项,此外,模拟通常也扮演桩的角色

测试的不同分类

根据范围分类

  • 单元测试:测试服务的一小部分,如类
  • 集成测试:验证服务是否可以与基础设施服务(如数据库)或其他应用程序服务进行交互
  • 组件测试:单个服务的验收测试
  • 端到端测试:整个应用程序的验收测试

根据测试象限分类

测试象限按两个维度对测试进行分类:

  • 测试是面向业务还是面向技术:使用领域专家的术语来描述面向业务的测试,使用开发人员的术语和实现来描述面向技术的测试
  • 测试的目标是协助开发还是寻找产品缺陷:开发人员使用协助开发的测试作为日常工作的一部分。寻找产品缺陷的测试旨在确定需要改进的部分。

根据象限分为四种不同的测试类别:

  • Q1协助开发 / 面向技术:单元和集成测试
  • Q2协助开发 / 面向业务:组件和端到端测试
  • Q3寻找产品缺陷 / 面向业务:易用性和探索性测试
  • Q4寻找产品缺陷 / 面向技术:非功能性验收测试,如性能测试

根据测试金字塔分类

测试金字塔的关键思想是,在金字塔从下往上移动时,应该编写的测试越来越少。

微服务架构中的测试挑战

进程通信是微服务架构的核心,基于微服务的应用程序是一个分布式系统,服务使用各种交互方式和进程间通信机制互相通信。因此测试验证API的服务是否能与其依赖关系和客户端进行正常交互尤为重要;

  • REST客户端 - 服务端:API Gateway将请求路由到服务并实现API组合
  • 领域事件使用者 - 发布者:Order History Service使用Order Service发布的事件
  • 命令式消息请求方 - 回复方:Order Service将命令式消息发送到各种服务并使用回复

一对服务之间的交互代表了这两个服务之间的契约或协议,契约要求双方就事件消息结构及其发布的通道达成一致。作为开发人员,需要确保服务具有稳定的API,做出的改动尽量不要破坏原有的API。测试两个服务可以交互的一种方法是同时运行两个服务,调用触发通信的API,并验证是否有预期结果,这会遇到集成的问题,解决方案是消费者驱动的契约测试。

消费者驱动的契约测试

消费者契约测试侧重于验证提供者API的参数定义是否符合消费者的期望。

消费者驱动的契约测试模式:验证服务是否满足它的消费者期望。

消费者契约测试模式:验证服务的客户端是否可以与服务通信。

使用Spring Cloud的契约测试服务

基于测试框架Spring Cloud Contract,以API Gateway与Order Service为例。

  1. 编写的契约包括API Gateway可能发送给Order Service的HTTP请求和预期的HTTP响应。
  2. Order Service团队(提供者)使用这些契约来测试Order Service,并使用它们来测试API Gateway,测试代码由Spring Cloud Contract生成。
  3. Order Service团队(提供者)将测试Order Service的契约打包成jar包发布到Maven存储库。
  4. API Gateway团队(消费者)使用已发布的契约为API Gateway编写测试

每个契约的请求和响应都扮演着测试数据和预期行为规范的双重角色,在消费者端测试中,契约用于Mock Order Service的行为,可以在不运行Order Service的情况下测试API Gateway。在提供端测试中,生成的测试类使用契约的请求调用者,并验证它是否返回与契约相匹配的响应。

部署流水线

其包含一系列执行测试套件的阶段,后面是一个发布或部署服务的阶段,理想情况下流水线是完全自动化的,也可能包含手动步骤。

部署流水线包含以下几个阶段:

  • 提交前测试阶段:执行单元测试。这是由开发人员在提交代码更改之前执行的。
  • 提交测试阶段:编译服务,执行单元测试,并执行静态代码分析。
  • 集成测试阶段:执行集成测试。
  • 部署阶段:将服务部署到生产环境中。

为服务编写单元测试

单元测试是测试金字塔的最低级别,他们是面向技术的测试,目标是协助开发。单元测试验证单元(服务的很小的一部分)是否正常工作。单元通常是一个类,因此单元测试的目的是验证这个类的行为是否符合预期。

两种类型的单元测试

  • 独立型单元测试:使用针对类的依赖性的模拟对象隔离测试
  • 协作型单元测试:测试一个类及其依赖项

类的职责及其在架构中的角色决定使用哪种单元测试

  • 控制器和服务类通常使用独立型单元测试
  • 领域对象(例如实体和值对象)通常使用协作型单元测试

为实体编写单元测试

为值对象编写单元测试

为Saga编写单元测试

Saga会实现重要的业务逻辑,其是一个持久化对象,向Saga参与方发送命令式消息并处理他们的回复。对Saga的测试除了要为正常执行的场景编写测试单元,还必须为Saga回滚的各种场景编写测试。

一种方法是编写使用真实数据库和消息代理以及桩服务的测试,以此来模拟各种Saga参与方;这种测试非常缓慢。

一种更有效的方法是编写模拟与数据库和消息代理交互的类的测试,这样可以专注测试Saga的核心职责。

为领域服务编写单元测试

服务的大多数逻辑通过实体、值对象和Saga实现。领域服务类实现业务逻辑的其余部分,测试这种类的有效方法是独立型单元测试,可以模拟存储库和消息传递类等依赖项。

为控制器编写单元测试

服务类通常具有一个或多个控制器,用于处理来自其他服务和API Gateway的HTTP请求。控制器类由一组请求处理程序方法组成,每个方法实现一个REST API端点。控制器的有效测试策略是模拟服务和存储库的独立型单元测试。

为事件和消息处理程序编写单元测试

与控制器类似,消息适配器往往是调用领域服务的简单类,因此可以使用类似针对控制器进行单元测试的方法。每个测试示例都是消息适配器,向消息通道发送消息,并验证是否正确调用了服务模拟。

个人理解:

单元测试,本质上就是以类或者单个服务、方法为单元,基于mock数据或者mock请求/响应的方式进行测试,核心是看单元的行为是否符合预期。

为了验证服务是否正确地与其他服务交互,必须编写集成测试和端到端测试,这将在下一章进行讨论。

本章小结

  • 自动化测试是快速、安全地交互软件的重要基石。更重要的是,由于微服务架构固有的复杂性,要从微服务架构中充分受益,必须实现自动化测试。
  • 测试的目的是验证被测试系统(SUT)的行为。在这个定义中,系统是一个泛指,意味着被测试的软件元素。它可能像一个类一样小,也可能像整个应用程序一样大,或者是介于两者之间,例如一组类或一个单独的服务。测试套件是一组相关测试的集合。
  • 简化和加快测试的一个好方法是使用测试替身。测试替身是一个模拟被测系统依赖项的行为的对象。有两种类型的测试替身:桩和模拟。桩是一个测试替身,它将值返回给被测系统。模拟也是一个测试替身,由测试用来验证被测系统是否正确调用依赖。
  • 使用测试金字塔可确定将测试工作重点放在服务的哪个部分。大多数测试应该是快速、可靠且易于编写的单元测试。必须尽量减少端到端测试的数量,因为它们写入速度慢、脆弱且耗时。
最近发表
标签列表