其他测试框架 微服务下的契约测试 (CDC) 解读

狂师 · 2017年11月10日 · 最后由 skytechfyh 回复于 2018年11月22日 · 4916 次阅读

1. 前言

有近两周没有在公众号中发表文章了,看过我之前公众号的读者都知道,公众号中近期在连载《RobotFramework 接口自动化系列课程》,原本计划每周更新一篇,最近由于博主在带一个新项目,实在是没空抽出时间来,所以向公众号中对连载课程一直期待的读者说声抱歉。

由于最近带微服务的项目,而对于微服务其实也是近从 14 年才流行起来,对于这块目前的干货内容还是较少,借着机会,小结一下知识点。所以今天也先不打算连载《RobotFramework 接口自动化系列课程》,如果读者对连载的课程比较热衷的话,可以在留言板下面给笔者留言,如果读者反馈较多的话,博主也会适当加快调整课程分享节奏。

下面给大家浅聊一下微服务架构下的契约测试。

2. 微服务特点

Microservice 微服务是一种架构风格,我们可以把每一个微服务视做一个用一组 API 提供业务功能的组件,且服务之间会有很多依赖关系,如下图所示:

这些服务之间可能由一个团队或者相互独立的团队开发和维护,并且它们在系统内部相互依赖,在这种情况下,接口的开发和维护可能会带来一些问题,例如服务端调整架构或接口调整而对消费者不透明,导致接口调用失败。

3. 微服务下的测试现状

例如, 我们想测试某微服务架构中的某一个服务时,比如下图第一排中间的服务,如:

因为它和其他服务都存在交互,一般我们有两种方式:

  • 部署所有的服务来实现端到端测试。
  • 在集成测试中 Mock 其他服务。

下面分析一下这两种方式优缺点:

对比分析 优点 缺点
第一种方式:部署所有服务 1、模拟生成环境
2、可以真实地测试服务交互
1、测试其中一个服务,不是不布署全部服务,包括各基础设施
2、要运行很长时间
3、不能及时给予测试反馈
4、测试环境被一个测试服务锁定,别人无法同时使用。
第二种方式:Mock 其它服务 1、测试反馈快
2、没有基础服务依赖要求
1、服务的实现方创建的 Stubs,可能实现与这个无关
2、无法模拟真实数据交互环境

4. 微服务下的开发现状

常规我们开发的项目主要由服务提供方约定接口,虽然提供方架构调整或改变接口之前通常会通知消费者,但可能还是会存在遗漏。

当一个 Service 已经同时被多个使用者调用用的时候,怎么保证 service 的修改对其它所有使用者造成影响被感知到呢?

5. 什么是契约测试

契约测试 ,又称之为 消费者驱动的契约测试 (Consumer-Driven Contracts,简称 CDC),根据 消费者驱动契约 ,我们可以将服务分为消费者端和生产者端,而消费者驱动的契约测试的核心思想在于是从消费者业务实现的角度出发,由消费者自己会定义需要的数据格式以及交互细节,并驱动生成一份契约文件。然后生产者根据契约文件来实现自己的逻辑,并在持续集成环境中持续验证。

后文中消费者驱动的契约测试统一用 cdc 来代替。

5.1 cdc 核心原则

  • cdc 是以消费者提出接口契约,交由服务提供方实现,并以测试用例对契约进行产生约束,所以服务提供方在满足测试用例的情况下可以自行更改接口或架构实现而不影响消费者。
  • cdc 是一种针对外部服务的接口进行的测试,它能够验证服务是否满足消费方期待的契约。 它的本质是从利益相关者的目标和动机出发,最大限度地满足需求方的业务价值实现。

5.2 常见测试框架

业界常用的 CDC 测试框架有:

  • Janus
  • Pact
  • Pacto
  • Spring Cloud Contract

6、契约测试、单元测试、接口测试之间的区别

  • API 测试和单元测试,更强调的是覆盖 API 内部逻辑。
  • 契约测试,更强调是组件之间连接的正确性,除了保证组件内部,还要保证组件间的调用是正确的,也就是服务 API 之间的调用。
类型 描述
单元测试 单元测试针对代码单元(通常是类)的测试,单元测试的价值在于能提供最快的反馈。另外好的单元测试还可以帮助你改善设计,在你的团队掌握 TDD 的前提下,单元测试能辅助重构,帮助改善代码整洁度。
API 测试 API 测试是针对业务接口进行的测试,主要测内部接口功能实现是否完整,比如说内部逻辑是不是正常,异常处理是不是正确。
契约测试 契约测试其实是为了测试服务之间连接或者说接口调用的正确性,为了验证服务提供者的功能是不是真正能够满足消费者的需求。它其实体现了测试前移的思想,把本来要通过集成测试才能验证的工作化作单元测试和接口测试,用更轻量的方式快速进行验证。
集成测试 它从用户的角度验证整个功能的正确性,测的是端到端的流程,并且加入用户场景和数据,验证整个过程是不是 OK,它的价值业务价值最高,是验证一个完整的流程。

7. 契约测试能解决什么问题

  • 联调成本过高,要双方开发到某一阶段后放在同一个环境上才能进行,要同时把握双方的进度,造成资源和时间上的浪费。
  • 对于接口的变动把控相当困难。由于接口变动是普遍存在的,尤其对于调用关系复杂的接口,一旦发生变动,如果没有一套机制进行控制,验证的成本巨大。 -“接口不匹配” 是指服务依赖于彼此间的接口进行通信,如何保证改变一个服务的接口会对其他所有依赖服务是否造成造成影响。
  • 在发生契约变化时,提供一种可立即被服务端和消费端发现的方式。

8. 契约测试能给我们带来什么

  • 降低服务集成的难度,把服务集成这个过程分解成了单元测试和接口测试来做,它从消费者的需求为出发点,把消费者的需求作为你的测试用例驱动出一份契约,然后验证提供者端的功能
  • 通过使用契约测试,接口调用双方协商接口后就可以并行开发,并且在开发过程中就利用契约进行预集成测试,不用等到联调再来集成调通接口,一旦成熟,在保证质量的前提下,联调的成本可以减低到几乎为 0。
  • 因为契约的存在,让接口的变动有迹可循,即使变动也可以确保变动的安全性和准确性。
  • 通过契约测试,团队能以一种离线的方式 (不需要消费者、提供者同时在线),通过契约作为中间的标准,验证提供者提供的内容是否满足消费者的期望。

9. 契约测试工具之 Pact

9.1 Pact 术语介绍

  • Consumer: 微服务接口的调用者
  • Provider: 微服务接口的提供者
  • 契约文件: 是由 consumer 端和 provider 端共同定义的接口规范,包括接口访问的路径,输入和输出数据。在具体的实施中,是由 consumer 端生成的一个 json 文件,并存放在 pact broker 上
  • Pact Broker: 保存契约文件的服务器

注:通常在工程实践上,当消费者根据需要生成了契约之后,我们会将契约上传至一个公共可访问的地址,然后生产者在执行时会访问这个地址,并获得最新版本的契约,然后对着这些契约来执行相应的验证过程。

9.2 Pact 基本流程

  • 第一步在消费者端 Consumer 端写一个对接口发送请求的单元测试,在运行这个单元测试的时候,Pact 会将服务提供者自动用一个 MockService 代替,并自动生成契约文件,这个契约文件是 Json 形式的。
  • 第二步在 Provider 端做契约验证测试,将 Provider 服务启动起来以后,通过 pact 插件可以运行一个命令,比如你是用 maven,就是 mvn pact:verify,它会自动按照契约生成接口请求并验证接口响应是否满足契约中的预期,所以可以看到这个过程中,在消费者端不用启动 Provider,在服务提供端不用启动 Consumer,却完成了与集成测试类似的验证。

基于消费者的业务逻辑,驱动出契约
  其实现步骤如下所示:
  1、使用 Pact 的 DSL,定义 Mock 提供者,如 localhost:8080
  2、将 Mock 地址传给消费者并对 Mock 的提供者发送请求。
  3、使用 Pact 的 DSL,定义响应内容 (包括 Headers、Status 以及 Body 等)。
  4、在消费者端 使用@PactVerification运行单元测试 (Pact 集成了 JUnit、RSpec 等框架),生成契约文件。
  5、当运行测试后,Pact 框架记录消费者的名称、发送的请求、期望的响应以及元数据,将其保存为当前场景下的契约文件,通常命名为 [Consumer]-[Provider].json,例如 orderConsumer-orderProvider.json
  6、契约文件生成后,我们可以将其保存在文件系统或者 Pact-Broker(Pact 提供的中间件,用来管理契约文件) 中,以便后续提供者使用。

基于消费者驱动出的契约,对提供者进行验证
  在提供者端,我们不需要写任何验证的相关代码,Pact 已经提供了验证的接口,我们只需要做好如下配置:
  1、为提供者指定契约文件的存储源 (如文件系统或者 Pact-Broker)。
  2、启动提供者,运行 PactVerify(Pact 有 Maven、Gradle 或者 Rake 插件,提供 pactVerify 命令)。
  3、当执行 pactVerify 时,Pact 将按照如下步骤,自动完成对提供者的验证:
4、构建 Mock 的消费者。
5、根据契约文件记录的请求内容,向提供者发送请求。
6、从提供者获取响应结果。
7、验证提供者的响应结果与 Pact 契约文件定义的契约中是否一致。

9.3 Pact 特性

传统情况下做集成测试需要把服务消费者和服务提供者两个服务都启动起来再进行测试,而 Pact 做契约测试时将它分成两步来做,每一步里面都不需要同时启动两个服务。

  • 测试解耦,就是服务消费与提供者解耦,甚至可以在没有提供者实现的情况下开始消费者的测试。

  • 一致性,通过测试保证契约与现实是一致性的。

  • 测试前移,可以在开发阶段运行,并作为 CI 的一部分,甚至在开发本地就可以去做,而且可以看到一条命令就可以完成,便于尽早发现问题,降低解决问题的成本。

  • Pact 提供的 Pact Broker 可以自动生成一个服务调用关系图,为团队提供了全局的服务依赖关系图。

  • Pact 提供 Pact Broker 这个工具来完成契约文件管理,使用 Pact Broker 后,契约上传与验证都可以通过命令完成,且契约文件可以制定版本。

  • 降低服务间的集成测试成本,尽早验证当提供者接口被修改时,是否破坏了消费者的期望。

  • 目前仅支持采用 REST 通信协议。

10. 特加申明

原文发布在本人公众号中,为了避免打广告,去掉与公众号相关信息

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
最佳回复

总结很赞!

共收到 9 条回复 时间 点赞

赞!
微信、博客园、testerhome 同步发布

槽神 回复

😁

在公司分享的时候听过契约测试,我很赞同这个理念,但实际上有操作难度,现在做法就是通过接口规约生成对应的代码模版,开发只能根据模版开发接口,客户端(消费方)也是按照既定的规约去做数据处理,比如像 google 的 grpc,规定 1 号位是传 A 字段,2 号位是传 B 字段这么做,契约测试真的很适合应用在微服务的产研测过程中,尤其是在经过垂直划分或横向划分的业务,从基础组件,业务组件,再到业务系统,这些如果用契约的理念规范起来,质量的确有保证

总结很赞!

陈恒捷 回复

😂 你也跟着在下面起哄

terrychow 回复

嗯,是的,我们现在的项目引入契约测试也是处于开始阶段,先小范围试点,套路摸熟后,再扩大布道范围。

狂师 回复

怎么是起哄,这段总结真的很赞嘛 😄

陈恒捷 回复

😬

terrychow [该话题已被删除] 中提及了此贴 04月04日 17:54

总结的很好,但是我有一个问题不是很明白,比如一个组价与另一个组件间的接口,是不是需要在保证两个组件间的连通性后才能做契约测试,不然怎么能拿到想要的契约文件呢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册