第04节:怎么针对微服务架构做集成测试?

在上一节中,我们讲解了怎么进行单元测试。虽然单独测试微服务内部的各个单元非常重要,但是,测试微服务的模块或者子系统能否正确地与外界交互也同样重要,这项工作可以通过集成测试来完成。这节课,我们将分别探讨在微服务架构下,集成测试的概念和实现方法,并分别以几个实例来说明。

集成测试(Integration Test)的定义有很多。简单来说,集成测试(也叫组装测试、联合测试)是单元测试的逻辑扩展,即把两个或者多个已经测试过的单元(见第三课中对于单元的定义)组合成一个子模块,再把这些子模块组合到一起,构成一个“子系统”。测试的目的就是检查,这些子模块能否以预期的方式互相协作,检查它们之间的通信和交互,核实接口是否工作正常,进而确保整个子系统的稳定运行。

虽然集成测试可以在很多级别上进行,但是在本课程所介绍的微服务架构中,集成测试主要是指:

  1. 微服务对外的模块(包括 HTTP 终端和 Gateway 部分)与外部服务(例如第三方支付、通知等)的通信;
  2. 数据库访问模块(Data Mapper/ORM)与外部数据库的交互。

也就是说,把对外模块与外部服务视为一个子系统,把数据库访问模块与数据库视为另外一个子系统,检查这两个子系统能否正常运行,从而确保整个微服务能够与外界正常交互。这两个子系统就是下图中用黄色虚线标出的部分:

image

对于第一种集成测试,因为服务与服务之间采用轻量级的通信机制互相协作(基于 HTTP 协议的 RESTful API)。所以在测试与外部的通信时,主要目的是确认通信是否通畅。注意,在这个阶段并不需要对外部服务做功能上的验收测试(Acceptance Test),因而只需检查基本的“核心功能”(Critical Path)即可。这种测试有助于发现任何协议层次的错误,例如丢失 HTTP 报头、SSL 使用错误,以及请求/响应不匹配等情况。

对于第二种集成服务,数据库访问测试旨在确保微服务所使用的数据结构与数据库相符。如果微服务使用了 ORM, 那么这些测试还可以检查 ORM 中 设置的映射关系,是否与数据库所范围的查询结果相符。因为大部分数据库都保存在网络之中,所以它们也会受到网络故障的影响。集成测试也需要考虑到检查数据库访问模块能否妥善地处理网络出错的情况。

在实现这些测试时,主要有以下三种手段:

  1. 使用实际的外部依赖服务;
  2. 使用模拟器模拟外部服务;
  3. 使用网络故障模拟工具来模拟不稳定的网络条件。

下面将举例说明这三种手段的实现方式。

使用实际的外部依赖服务

下面我们来举一个实例。假设我们的微服务需要依赖于外部的一个记录书籍信息的服务 Book API,其网址终端(Endpoint)的功能定义如下:

请求类型

URL

功能说明

GET/books查询书籍列表
POST/books创建书籍
GET/books/id根据 ID 查询一本书籍
PUT/books/id根据 ID 更新一本书籍
DELETE/books/id根据 ID 删除一本书籍

请注意,集成服务是整个 CI/CD(持续集成/持续交付)管线的组成部分。但是,在使用实际外部服务进行集成测试时,测试结果很容易受到网络因素的影响,譬如网络延时较长等。这样会导致误报和不确定性,为了避免整个项目管线因此而中断,建议单独为集成测试建立一个单独的管线。

使用模拟器模拟外部服务

采用实际的外部依赖服务虽然很能真实反映产品质量,但是在实践中,经常发生的情况是,外部依赖尚未开发完毕,或者不稳定,那么为了完成集成测试,就必须采用模拟器(即上节课所提到的 Mock 或者 Stub)。另外,如果需要测试子模块在外部服务出现异常时的行为,也需要模拟器来模拟外部服务的异常状态,例如响应超时。

通常在集成测试阶段,用来做模拟外部依赖服务的工具包括:

下面我们将以 WireMock 为例,说明怎么通过模拟器来模拟外部依赖的服务,完成集成测试。

WireMock 是一种针对 HTTP API 的模拟器,也被视为一种服务虚拟化工具或者模拟服务器。它适合在所依赖的外部 API 不存在或者尚未开发完毕时,让开发人员依然能够继续工作。它还可以用于测试实际 API 很难达到的边界情况(例如极端值)或者故障模式。

运行 WireMock 可以有两种方式,一种是使用 Maven 等项目管理工具,则需要在配置文件的依赖部分加入下面内容:

另外一种是独立运行,即从这里下载独立安装包,然后在 Java 环境下直接运行:

下面这行 Java 代码将设定:当相对 URL 严格为 /some/thing(包括查询参数)时,返回状态值200。响应内容为“Hello World!”。如果相对 URL 为 /some/thing/else,则返回状态值404。

另外一种方法是用一个 JSON 文件来模拟 API。要达到和上面相同的效果,只需要把包含以下内容的 JSON 文件,发布到:http://<host>:<port>/__admin/mappings,或者放在 mappings 目录下面:

除了独立运行,WireMock 也可以直接嵌入到代码中。最方便的就是在 JUnit 中使用,WireMock 提供了 WireMockRule, 可以很方便的在测试时嵌入一个 Stub 服务。

下面就是一个支付相关的集成测试,被测方法需要调用微信的支付服务。stubForUnifiedOrderSuccess 设置了一个很简单的 Stub(模拟器),一旦匹配到请求的 URL 为 /pay/unifiedorder,那就返回指定的 XML 内容。这样,就可以在集成测试里测试整个支付流程,而不必依赖真正的微信支付服务。当然,测试时微信支付接口的 Host 也要改成 WireMockRule 配置的本地端口。并且,通过这种方式也很容易测试一些异常情况,即根据需要修改 Stub 返回的内容即可。

有时候在集成测试里,还需要验证系统的行为,例如是否调用了某个 API,调用了几次,调用的参数和内容是否符合要求等。区别于前面说的 Stub,其实这就是常说的 Mock 功能。WireMock 对此也有很强大的支持。

由此可见,借助 WireMock,集成测试时处理第三方的依赖就变得非常方便。不需要直接调用依赖的服务,也不需要专门创建用于集成测试的 Stub 或 Mock,直接代码中根据需要设置即可。

简而言之,我们可以借助 WireMock 这样的模拟器工具,优化开发流程:

  • 在外部服务尚未开发完成时,模拟服务,方便开发。
  • 在本地开发时,模拟外部服务避免直接依赖。
  • 在集成测试中模拟外部服务,同时验证业务逻辑。

使用网络故障模拟工具来模拟不稳定的网络条件

因为大部分集成测试都涉及到网络连接,所以必须确认服务或者模块能够妥善地处理网络故障(例如速度很慢或者超时)等情况,这意味着人为地制造一些网络故障,以测试对外模块在这种情况下的响应。这里推荐一个工具:clumsy。通过封装 Winodws Filtering Platform 的 WinDivert 库,clumsy 能实时地把系统接收和发出的网络数据包拦截下来,人工造成延迟、掉包和篡改操作后,再进行发送。无论是要重现网络异常造成的功能问题,还是评估微服务在不良网络状况下的表现,clumsy 都能在无需额外添加代码的情况下,在系统层次达到想要的效果。

它的功能包括:

  • 下载即用,无需安装。
  • 不需要额外设置,不需要修改应用代码。
  • 实现系统级别的网络控制,可以适用于命令行、图形界面等任何 Windows 应用程序。
  • 不仅仅只支持 HTTP,任何 TCP、UDP 的网络连接都可以被处理。
  • 支持本地调试(服务器和客户端都在 localhost)。
  • “热插拔”,你的程序可以一直运行,而 clumsy 可以随时开启和关闭。
  • 实时调节各种参数,详细控制网络情况。

clumsy 会先根据用户选择的 filter 来拦截指定的网络数据。在 filter 中可以设定感兴趣的协议(TCP/UDP)和端口号,接收还是发出端口,也可以通过简单的逻辑语句来进一步缩小范围。当 clumsy 被激活时,只有符合这些标准的网络数据会被进行处理,其他数据仍然会由系统正常传输。

当被 filter 的网络数据包被拦截后,你可以选择 clumsy 提供的功能来有目的性地调整网络情况:

  • 延迟(Lag),把数据包缓存一段时间后再发出,这样能够模拟网络延迟的状况。
  • 掉包(Drop),随机丢弃一些数据。
  • 节流(Throttle),把一小段时间内的数据拦截下来后再在之后的同一时间一同发出去。
  • 重发(Duplicate),随机复制一些数据并与其本身一同发送。
  • 乱序(Out of order),打乱数据包发送的顺序。
  • 篡改(Tamper),随机修改小部分的包裹内容。

这样,开发人员就能够模拟出不稳定的网络状态,调试微服务对于异常网络的响应。

本节总结

微服务架构下的集成测试主要包括两种:

  1. 微服务对外的模块与外部服务的通信;
  2. 数据库访问模块与外部数据库的交互。

要实现这两种集成测试,主要有三种手段:

  1. 使用实际的外部依赖服务;
  2. 使用模拟器模拟外部服务;
  3. 使用网络故障模拟工具来模拟不稳定的网络条件。

在单元测试通过的基础上,集成测试进一步完善了测试覆盖率:不仅微服务内的模块可以正常工作(根据单元测试的结果),而且这些模块也可以正常地组合到一起发挥作用,并与外部进行通信和交互。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值