什么是契约测试

单身俱乐部目前有 2 个人。A 很宅,几乎不认识任何异性。P 人脉很广,有很多人的信息,并且他会把自己的通讯录用固定的格式保存起来。

{
	"name": "lisa",
    "age": 18,
    "height": 172,
    "address": "xxx city",
    "salary": 22000
}

A 和 P 说,你能把你的数据卖给我吗? P 很高兴的答应了,并且和 A 签订合同(契约),每次 A 发起请求,P 就按约定给出姓名和年龄的数据。A 是数据的消费者(Consumer),D 是数据的提供者(Provider)。

P 发现这个生意很好做,自己提供的服务非常单一(微服务),但是有非常大的需求,有很多人愿意和自己签订合同,他们每个人需要的数据各不相同,所以每份合同都不一样。

有一天,老客户 A 找到 P,他需要名字分成 first name 和 last name,D 当即答应,马上将自己的数据存储格式进行了修改。

现在 A 能够非常方便的获取到自己想要的数据,但是客户 C 的数据缺收到了影响,他得到的数据结构不是按照合同里约定的格式了,自己得到的数据发生了变化,于是告 P 毁约。

如果 P 只有一个 consumer A,他们直接修改合同就可以了,通常不会造成毁约现象,也就不需要用到所谓的契约测试。但是在微服务这样的架构中,一个 provider 往往对应多个consumer,当其中某个 consumer 提出修改契约后,往往会对其他的 consumer 造成影响,此时就需要用到契约测试了。

契约测试就是:当 provider 的代码改动以后,它产生的数据结构变化不会对任何 consumer 产生毁约现象,在微服务架构中可以使用,在单体架构中照样可以使用。当测试到即将毁约的情况,需要提醒对哪些 consumer 造成了影响,此时必须协商,多方达成一致,才能继续履行契约。

实际上除了契约测试,传统的测试方式也可以达到同样的测试目的?

首先来看看第一种,consumer 可以对 provider 的服务进行测试,一般就是以直接访问API 作为测试形式。当发现测试不通过时,通知 provider 。

但是这种方式除了针对数据结构设计用例,还会有其他非常多的异常用例,执行效率很慢,找到问题再加上需要多方协商,更慢。 契约测试只需要保证数据结构是正常的就可以了,并不需要对数据的值进行测试,契约测试也只需要考虑正向用例能否引发异常,不需要考虑异常用例。

第二种是集成测试,consumer 可以对自己服务和 provider 设计集成测试,模拟用户发送请求给 consumer 服务,consumer 再调用 provider 服务。

这种方式比第一种方式更慢,而且他不能精准的定位是 Consumer 自己引发的问题,还是 provider 引发的,需要进一步排查。

这两种测试方式都是由 consumer 发起的,而不是 provider 。难道 provider 自己不测试自己的服务吗?他们当然会测,只是他们只能测试自己的单个组件,还有他们依赖的上层服务,但是他们无法测试 consumer 的逻辑,因为他们根本不知道 consumer 是怎么使用他们服务的。

契约测试虽然被测对象是 provider ,但是需要保证的却是 consumer 的业务。

我们来总结一下,为什么需要专门的契约测试。

第一点,你可以想象一下,你就是 provider。 当你修改完代码之后,什么时候可以确定自己修改的代码质量没有问题呢? 首先,你得找到有哪些 consumer 依赖了自己的服务,然后,你只能祈祷所有的 consumer 快点完成他们的测试,至于到底什么时候能完成,你只能多走几遍跨部门协作的流程,多催催他们。 实际上,因为每个服务的复杂程度不一样,所以测试的效率会有很大区别。

以上对于 provider 的接口和集成测试,虽然是由 consumer 驱动的,能够保证 consumer 的质量,但是 provider 只能被动接收由多个 consumer 组成的测试结果。

第二点,无论是传统的接口测试还是集成测试,不仅需要检测数据的结构,而且需要检测测试的数据,而契约测试通常只需要检测数据结构,查看是否包含了 name 和 age 等 consumer 需要的字段。

契约测试是专门测试数据结构的综合性测试方法。通常来说,它由 consumer 发起测试,然后将测试数据形成契约文件发送给 provider ,由 provider 根据契约文件执行测试,这样 provider 就能自己控制测试进度,主动获取结果。

在形式上比较类似接口测试,因为它们都是通过访问服务来确定是否通过测试,但接口测试需要关注全部的调用场景,比如正向和异常场景,而契约测试一般只关注正向的消息结构是否正确。

对于一个返回的消息,契约测试关注它有多少个字段、每个字段的值是什么类型,但是一般不会去校验字段的值到底等于多少。所以契约测试一般不需要关注数据,而是重点关注数据的结构(schema)。

参考: