集成测试痛点
在软件集成测试阶段,两个服务之间相互调用不通的情况时有发生,大多时候甚至是scheme定义不一致,更的时候是因为相互依赖的服务其中一方发生变化或更新,另一方不能及时感知造成,这时便需要一种方式可以同时约束服务双方的变化并让另一方快速知道以便能及时响应变化确保软件正常工作。契约应运而生。
契约由服务的消费方和提供方事先约定而成,并由指定的一方生成给另一方,另一方在开发接口时运行测试以验证开发出来的接口满足契约要求,Pact则是契约测试框架中的一种,下面以此为例介绍如何做契约测试。[选型-Pact与其它工具的对比](https://docs.pact.io/getting_started/comparisons)。
如何用Pact实现契约测试
在Pact中,契约由Consumer生成,由Provider去验证,一个基于consumer和provider的契约被称作一个pact,而每个pact是一系列的api交互集合
契约生成
契约由Consumer的单元测试生成
- 生成原则:
每个pact是一系列的api交互集合,所以pact的生成原则也就等同于一个api交互的原则
- 包含一个期望的request
- 一个期望的最小单元的response
- 大多数时候接口的调用是基于某一条件或状态的,所以也需要要定义一个provider states
describe('Pact with Order API', () => {
describe('given there are orders', () => {
describe('when a call to the API is made', () => {
before(() => {
return provider.addInteraction({
state: 'there are orders',
uponReceiving: 'a request for orders',
withRequest: {
path: '/orders',
method: 'GET',
},
willRespondWith: {
body: eachLike({
id: 1,
items: eachLike({
name: 'burger',
quantity: 2,
value: 100,
}),
}),
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
},
})
})
it('will receive the list of current orders', () => {
return expect(fetchOrders()).to.eventually.have.deep.members([
new Order(orderProperties.id, [itemProperties]),
])
})
})
})
})
- 契约文件存放
Consumer将生成好的契约文件存放在指定地方,以便Provider能取得到。Pact提供了Pact Brokers来解决此问题,后文会讲到
契约验证
契约由Provider的单元测试验证
在provider的单元测试中,首先读取当前pact所依赖的状态,比如数据库日期字段需要晚于2022-10-13等,通过mock单元测试调用provider接口,拿单元测试的返回值和pact所描述的定义值进行比对看是否满足契约。
Pact Broker - Pact的管理
在实际应用中,我们可能需要频繁对接口微调以满足新需求,这时候实现契约版本管理尤为重要。Pact broker不但可以用于契约版本的管理,其实也是作为consumer,pact和provider三者的一种关系映射,能够记录当前每一方的版本号,同时在provider方给出对应版本的契约在provider中的运行结果,具体可参考 [pact_broker](https://docs.pact.io/pact_broker)。