聊一聊契约测试

发表于:2018-5-30 11:13

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:崔彦松    来源:洞见

  什么是契约
  如果从契约产生的阶段来说,现有资料表明最早要追溯到西周时期的《周恭王三年裘卫典田契》,将契约文字刻写在器皿上,就是为了使契文中规定的内容得到多方承认、信守,“万年永宝用”。所以订立契约的本身,就是为了要信守,就是对诚信关系的一种确立。诚信,是我国所固有的一种优良传统,也是延续了几千年的一种民族美德,在中国儒家的思想体系里,是伦理道德内容中的一部分。
  《现藏于台北故宫博物院》
  现实真的是那么美好吗?小时候的价值观教育未能改变社会的现状,缺少契约精神的案例却比比皆是。
  那么,契约真的要消失了吗?不尽然,在软件测试领域,我们又重新拾起了契约这把利器。
  发展历程
  接下来让我们把时间回溯到2011年初,回到老马的文章《集成契约测试》中来,回顾一下契约测试的起源和发展历程:
  假设我们有这样一个场景:A团队负责开发API服务,B团队进行API调用消费服务。
  为了保证API的正确性,我们会对外部系统的API进行测试(除非你100%相信外部系统永远正确和保持不变),这很可能就会导致一个问题,当外部系统并不那么稳定或者请求时间过长时,就会导致我们的测试效率很低,并且稳定性下降。比如当外部API挂掉导致测试失败时,你并不能完全确信是API功能被更而改导致的失败还是运行环境不稳定导致的请求失败。
  最初,解决这个问题的方案是构建测试替身(Test Double),通过模拟外部API的响应行为来增强测试的稳定性和反应速度。实现手段是在测试环境中搭建一个模拟服务环境,通过设定一些请求参数来返回不同的响应内容,然后再被内部系统调用,来保证调用端的正确性。构建模拟环境时我们可以使用几种不同的测试手段,如Dummy,Fake,Stubs,Spies,Mocks等。可是,问题又来了,如果使用测试替身那如何能保证外部系统API变化时得到及时的响应,换句话说,当内部系统测试都通过的通过时,如何能保证真正的外部API没有变化?
  一个比较简单的方式是部分测试使用测试替身,另外一部分测试定期调用真实的外部API,这样既保证了测试的运行效率、调用端的准确性,又能确保当真实外部系统API改变时能得到反馈。
  是不是到这里就皆大欢喜了呢?
  如果剧情到这里就结束的话,未免太过俗套。这个方案最大的缺陷在于API的反应速度,真实外部API的反馈周期过长,如果减少真实API测试间隔时间就又会回到文章最开始的两难境地。
  那么如何解决这个问题呢?先来让我们剖析一下前面几种解决方案的共通点。
  在上面的场景中,我们都是已知外部API功能来编写相应的功能测试,并且使用直接调用外部API的方式来达到验证测试的目的,这样就不可避免的带来两个问题:
  第一,服务消费方对服务提供方API的更改是通过对API的测试来感知的。
  第二,直接依赖于真实API的测试效果受限于API的稳定性和反映速度。
  解决方式首先是依赖关系的解耦,去掉直接对外部API的依赖,而是内部和外部系统都依赖于一个双方共同认可的约定—“契约”,并且约定内容的变化会被及时感知;其次,将系统之间的集成测试,转换为由契约生成的单元测试,例如通过契约描述的内容,构建测试替身。这样,依赖契约的测试效率优于集成测试,同时契约替代外部API成为信息变更的载体。
  对于契约来讲,行业内比较成熟的解决方案是基于YAML标记语言的Swagger Specification(OpenAPI Specification),或者是基于JSON格式的Pact Specification。
  通常的做法是API的提供者使用“契约”的形式,将功能发布在公共平台,给调用方进行说明和参考,这里我们可以暂时称之为Provider-Driven-Contract。这种做法的潜在问题是,功能提供方的API返回内容是否都满足所有API调用者的需求不得而知。所以,针对这个问题,依赖关系再一次反转,契约测试就摇身一变成为了Consumer-Driven-Contract test(CDCT), 通过给API提供方提供契约的形式,来完成功能的实现。
  难道CDCT成为了问题终结者吗?请听后面分解。
  注: 契约测试其中一个的典型应用场景是内外部系统之间的测试,另一个典型的例子是前后端分离后的API测试,这里不做过多展开。
  契约测试的维度
  1.测试覆盖范围对比(纵向)
  单元测试:对软件中的基本组成单位的测试,大多数是方法函数的测试,运行速度快。
  契约测试:对服务之间的功能进行的测试,运行速度基本与单元测试相同。
  E2E 测试:对系统前后端或者不同系统之间的集成测试,大多通过模拟UI操作的方式实现,运行速度三者之中最慢。
  2.测试效率对比(横向)
  环境依赖:
  单元测试:程序集
  契约测试:程序集、依赖契约文件、虚拟路由服务
  端到端测试:程序集、真实路由服务、前端UI
  运行速度: 单元测试 > 契约测试 > 端到端测试
  Pact官方给出的几个场景:
  适用场景:
  团队能把控开发过程中的Consumer和Provider端
  适合Consumer驱动开发的场景
  对于每个独立的Consumer端,Provider端都能管理好需求。
  不适用的场景:
  公共API或者是OAuth授权服务
  Provider端和Consumer端没有良好的沟通渠道
  针对性能的测试
  Provider端的功能性测试(Pact只测试内容和请求格式)
  对于不同输入有相同的输出,并未达到验证的目的
  当前测试输入需要依赖之前测试返回的结果
  以上对比说明契约测试所要解决的问题是替代系统之间的集成测试,通过契约和单元测试的方式加速系统运行。同时也说明契约测试存在一些不适用的场景,要依据使用场景区别对待。契约测试没有取代单元测试以及E2E测试。



上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号