微服务架构的系统中,存在着大量的服务,每个服务开放出接口(作为provider),接口可以被很多其他服务调用(consume)。接口API是服务提供者和服务消费者之间的契约,理论上,只要测试了双方的实现都完全符合API,就可以保证它们能够正常的集成到一起;但在现实场景中,对API难以给出非常精确的定义,覆盖测试完整API的工作量也会非常巨大,再加上随着API的演进,要验证兼容性更是很麻烦。
消费者驱动契约(Consumer Driven Contract)测试试图解决这个问题。它从接口的消费者出发,记录下消费者使用接口的各种场景,以此作为契约,验证接口的提供者是否符合。Pact 就是支持针对 HTTP API 的消费者驱动契约测试的工具。
传统上会将测试做以下分类:
- 单元测试:测试单个service
- 集成测试:测试由多个services组成的系统
- 端到端测试:测试从用户到各个外部系统的整个场景
契约测试是对单元测试的增强,针对服务接口provider测试,覆盖了一部分本来需要集成测试才能测试到的场景。
假设我们有一个模块 P 提供了某个API,这个API被其他三个模块 C-1、C-2、C-3 使用了。集成测试需要搭建起一个环境,把这几个模块都部署起来,作为一个整体来测试,验证它们之间的 API 调用是否正确。

要对这些模块独立进行单元测试,就需要mock API 的 provider 或者 consumer。每个绿色框框都是独立的测试环境,相互没有依赖。但问题是,对 P 的测试,要覆盖 API 的各种调用场景,需要构造出很多测试用例,分别不同的设置 mock 的请求和期望响应。这样不但很繁琐,而且还很难确保已经覆盖了各种不同 consumer 的真正实用场景。

引入了pact之后,对于 consumer 的测试区别不大,依然要写各种测试用例,给出 API 调用的期望请求和对于响应,只不过是换成用 pact 工具来做mock。在测试执行过后,pact 会将 API 请求以及响应(测试用例中模拟的)记录到 pact file(JSON格式)中,作为消费者端的契约。
对于 provider 的测试就不同了,不需要再构造测试用例来测试 API,而是将 pact files 作为输入,pact 会跟进消费者契约生成 API 请求,并且检查 provider 的响应是否符合契约。

使用了 pact 之后,依然是每个服务独立的进行单元测试,但是可以模拟出真实集成场景。
Pact 还提供了 pact broker 去管理 pact files,以便方便的发布、获取契约,了解服务之间的依赖关系。对于由非常多服务组成的复杂系统,会有很大帮助。
但是,使用消费者驱动契约测试是有限制的,只适用于测试系统内部服务:
- 消费者、提供者都使用同样的契约测试工具进行测试
- 服务的消费者必须是可全部罗列的,所有服务消费者的测试用例需要完备,覆盖它调用 API 的所有场景,否则服务提供者的测试覆盖就会不足
Read more: