契约测试 pact 实践

2018-03-18  籽藤 

下文来自我在公司内部分享的 pact 实践。主要是面向开发人员的分享。目前听到的反馈还不错,期望能带给更多人启发。

需要说明的是,文中我基于官方 demo 为我们现有项目写了 pact tests 作为演示,这里就不便公开了,只能贴一些执行结果截图,大家感受一下。看完还是去啃官方文档吧 :)

========我是正文分割线=========

接口开发过程的协作问题

众所周知,目前的 Payment 系统存在多个后台模块,相互间的 API 调用非常多。如下图,往往模块 A,B,C 是由不同的开发人员维护的,使用的语言也不一样,而现在的测试方式,主要是依赖测试人员通过最源头的入口 Endpoint 进行测试,如果发现了问题,再通过一层层的日志,发现到底是哪个模块出了问题。这类测试是当然必不可少,但是把问题都集中在测试阶段发现,这样的反馈周期显然不够快。


其实,在开发接口的过程中,开发童鞋需要关注两类问题:模块内部逻辑,以及上下游接口的变更。

模块内部逻辑的测试,要提倡开发童鞋写 Unit Tests,这里不再赘述;而上下游接口的变更,过去我们会使用 Mock 工具。但使用一般的 Mock 工具还会面临以下问题:

  • 一个 URL,不能对应多个 Response。比如查询 User Balance 接口,上下游模块之间要约定好“查询成功”和“失败”等多个情况下的返回格式是怎样的。

  • Mock 信息的版本管理。比如,谁有权限更新 Mock Server 配置的接口信息?如何将 Mock Server 的接口配置信息与开发模块的版本对应起来?

  • 上下游通知机制。张三将变更情况告知了李四,李四可能忙忘记了,忘记改代码了;也可能在沟通过程中,接口变更细节并没有真正理解到位,上下游双方的认知不一致。

针对上述上下游模块交互的情况,业界推出了 “契约测试”(Contract Tests)的概念,pact 正是用于契约测试的。https://docs.pact.io/

Pact 使用场景

Pact 工作的核心,是 Consumer Driven Contracts testing。本质上也是一种 Mock 服务,但是比一般的 Mock 框架考虑得更为全面。

针对上述的三类问题,Pact 是这样解决的:

  • Provider 一个接口可以设置多个 Provider states ( Response),通过在 Provider 注入数据,来匹配 Consumer 响应。

  • Pact tests 跟开发代码在一个项目中。可以在本地生成接口相应的 json 文件(也就是上下游之间的契约),方便与代码一起管理,也可以上传到 Pact Broker 中,通过 Web 页面来查看管理。

  • Pact tests 很方便做持续集成。也就是说,可以把 pact tests 的运行配置到 Jenkins Job 中,Consumer 和 Provider 项目有变更都需要跑 pact tests,如果失败了会自动通知相关人员。

每个工具都有自己的适用场景,我们不能期望一个工具来解决所有问题。

适用场景

  • 消费者( Consumer )和 提供者( Provider )均是由同一组织提供。所以,Ping++ 和支付渠道或第三方服务之间的接口就别考虑 pact 啦~

  • Provider 提供的功能,往往是因为 Consumer 的需求来推动的。

  • 一个 Provider 的 Consumer 数量不多,Provider 知道有哪些 Consumer 模块会消费。

以下场景中,Pact 难以发挥作用

  • Provider 和 Consumer 团队之间难以进行良好的沟通。

  • 测试新功能,或现有的 Provider 功能不会根据 Consumer 的需求变更所调整。比如第三方服务的 API,OAuth Provider。

  • Provider 逻辑的正确性测试。功能逻辑的自测可以通过 Provider 自身的 Unit Tests 来进行,pact 不太适合。

  • 性能和压力测试。

  • “pass through” 接口测试。如果 Provider 只是将请求内容传递给下游服务(比如 消息队列),上下游之间并不关心 body(可以是任意内容)的话,也不用考虑 pact

Pact 工作原理

如下图(图片来自官方文档),简单来说,pact 的工作分为两步:

  1. Consumer 向 pact 发请求。pact 会根据响应,生成 json 文件(也就是 Contract)。这个步骤的完成,就意味着 Consumer 的需求很明确了。

  2. pact 向 Provider 发请求,将会以 json 文件中的内容作为期望结果,与 Provider 返回的结果进行匹配验证。

至于 Consumer,Provider 和 pact 的交互,就是我们要在代码中定义的。后文将以模块 AAA 和 BBB 为例进行说明。

Go 模块使用 pact 示例 

AAA 模块会调用 BBB 模块的接口,查询某一个 App 下某个 User 的余额信息。

1. 准备工作 

下载 pact-go ,运行 pact-go daemon

https://github.com/pact-foundation/pact-go#installation

2. Consumer ( 模块 AAA )

下图源自:https://www.codeproject.com/Articles/1180045/Consumer-Driven-Contract-Testing-with-PactNet-Csha


2.1 编写 tests

(在此略过)

2.2 执行 tests

应用了 patch 之后,在 tests 目录下执行命令:go test -v .

2.3 查看生成的 json 文件

可以很清楚地看到对请求和响应地定义,同时设置了匹配规则( matchingRules ) 为 type。

目前支持的 matchers 参见: https://github.com/pact-foundation/pact-specification/tree/version-4

目前 pactSpecification 版本是 2.0.0

3. Provider( 模块 BBB)

下图源自:https://www.codeproject.com/Articles/1180045/Consumer-Driven-Contract-Testing-with-PactNet-Csha


3.1 获取 Contract

目前没有搭建 Pact Broker,可以把上面生成的 json 文件拷贝到 BBB 项目中。

3.2 编写 tests

(在此略过)

3.3 执行 tests

在 tests 目录下执行命令:go test -v .

1105°/11039 人阅读/2 条评论 发表评论

文晶  2019-01-11 1

rmi 的契约有做么


籽藤  2019-03-21 1

@文晶 没有在项目里玩过诶,具体是啥问题呢


登录 后发表评论