• 0
  • 0
分享
  • 使用契约先行开发减少契约测试​​——软件测试圈
  • quinn 2024-09-04 16:00:47 字数 3844 阅读 145 收藏 0

  契约维护的难题

  如今微服务凭借其灵活、易开发、易扩展等优势深入人心,不同服务之间的集成和交互日渐繁多且复杂。这些服务之间交互的方式是多样的,常见的有 HTTP 请求和消息队列。在它们交互的过程中,会有服务的版本演进,交互信息的格式或方式就会产生变化,前后版本的接口可能并不兼容,甚至开发环境经常会宕机更新,加之不同服务的开发进度有快有慢,各团队的优先级有高有低,在开发过程中,服务间交互方式的匹配性就成了一个问题。

  这里,不同团队之间,对服务间如何进行发送和接受消息所能达成的共同理解,我们称之为契约 (contract)。如何采用一个合理的机制,维护服务间契约,使服务提供方和消费房能够在不造成事故的前提下,保持各自的高效开发,越来越成为各团队日常开发中要面对的问题。

  契约测试

  契约测试 (contract testing) 就是在这样的背景下应运而生,以下引用 Pact 官网的定义:

  Contract testing is a technique for testing an integration point by checking each application in isolation to ensure the messages it sends or receives conform to a shared understanding that is documented in a "contract".

  契约测试是一种测试集成点的技术,它通过隔离检查每个应用程序,以确保其发送或接收的消息,符合记录在“契约”中的共同理解。

  也就是说,在测试己方服务时,通过使用测试替身 (test double),让它能够模仿我们所依赖的外界系统,返回相对真实的消息响应,从而让我方团队在尽可能保证与外界系统兼容的前提下,避免受到外界系统宕机或开发新版本等影响,提升开发效率。再结合消费者驱动开发的优势,可以避免服务提供端浪费精力去实现不必要的功能,因此,很多团队采用了消费者驱动的契约测试 (consumer-driven contract test) 的实践。

1-1.png

  在契约测试的帮助下,很多团队真正提升了开发效率,掌握了自己的节奏,但也有些团队发现效果并不明显,因为契约测试带来的收益并不是免费的。契约测试有着不少的开发成本,每有一个新的需求,新的接口,新的字段,或是老字段的可空性发生了改变,以及枚举值的增加或减少,都需要增加或减少一些测试用例,然后在测试的过程中生成新的契约。这些契约大多以 OpenAPI 文档的形式存在,作为两个团队日后讨论的基准。

  契约测试驱动的合作流程

  消费方与提供方沟通,达成基本契约:增加一个接口,调用的时候传一个 RequestDto,接口返回一个 ResponseDto,其中 RequestDto 和 ResponseDto 哪几个字段要非空。

  消费方回去写自己的契约测试,生成契约 (通常以 OpenApi doc 形式),然后以契约测试驱动,开发自己的逻辑。

  服务方拿到生成的契约,进行测试驱动开发,验证契约是否被满足。

1-2.png

  契约测试有时修改代价高

  在使用契约测试时经常有这样的感受:比如,一些简单的契约,比如非空字段和格式校验等,每种情况都要专门的测试用例,而实现它却只需要一个注解,有点得不偿失。

  更重要的是,随着测试的编写,生成的契约可能比当时商讨的更为简单,比如一些 400, 401 等情况,有时并不会为每一个 API 写足够细节足够详尽的测试;也可能生成的契约比商讨的更为详细,比如消费方在编写契约测试的过程中考虑到了更多的边缘场景。因而每当由以上情况导致修改契约时,我们都会重新沟通,再等待消费方重新写契约测试,再生成契约,然后服务提供方再开发。这个从沟通到落地的闭环比较长,每次修改时,服务提供方需要等待消费方编写测试,生成契约,然后消费方等待服务方按照契约开发完成,才能发布新的版本,这些等待都会拉低开发效率。

  解决的思路

  反馈周期长是我们经常需要面对的问题,比如我们要搞快速迭代,定期 showcase,就是为了及时得到客户的反馈;再比如我们搞结对编程,也是为了将 pull request 上可能出现的沟通提前,避免盲目开始之后又造成返工。这个时候我们再看开发流程时就会想,如果第一步的沟通可以直接产出固化的契约,足够直观和详尽,让双方及早沟通,同时又可以使用自动化的方式约束消费方和服务方双方,省掉重复琐碎的契约测试,让沟通过后双方都可以直接开始开发就好了。

  契约先行开发

  这里说的契约先行,指的是在写所有代码之前,把我们沟通好的契约手写出来,或者通过一些图形化工具生成出来,比如手写或生成一个 yaml 格式的 OpenAPI 文档,这样沟通的产出足够直观。把这个文档放在代码库中维护,然后以它为依据,通过自动化流水线生成各方的沟通组件(sdk),既能使双方同时开始开发,又能对双方进行约束。

  以下以 Java + Spring 为例,通过使用 OpenAPI generator 工具生成代码,达到以上效果。

  对于消费方来说,流水线可以根据定好的契约,生成封装好的 client 类,它提供一个简单的方法,这个方法包含了 RestTemplate 的参数和逻辑,以及对应的 RequestDto/ResponseDto。消费方开发人员只需要关注己方业务需要,将合适的参数传给该方法,无需关注这些参数是用于 path 还是用于 body,该方法会以合适的方式与服务方沟通。同时我们可以定制化生成的方法,对非空字段进行校验,达到约束效果。

  对服务方来说,流水线可以生成对应的 Controller 组件,http path 和方法的匹配等,服务方只需要复写相应的方法来完成自己的业务需求,无需关心传进来的参数是属于 path、header 或者是 body。由于定制化生成的 server 端 sdk 严格根据契约生成了合适的注解,比如 @NotNull?, @Size?, @Pattern 等,Spring 可以自动对注解进行校验,server 端就可以自动拒绝不符合契约的请求。同时,由于生成的 ResponseDto 也带有响应 validation 注解,我们也可以对服务端返回的 ResponseDto 进行约束。

  契约先行模式下,团队的沟通闭环

  这样以来,团队的合作流程如下:

  1. 消费方与提供方沟通,达成契约,并在契约代码库中一起提交契约代码,即 OpenAPI doc。然后触发流水线,生成各方代码 sdk。

  2. 双方各自引入 sdk 进入开发。

1-3.png

  通过观察以上流程可以发现,与契约测试相比,该流程提前对沟通结果进行了直观的固化,使双方基于细节的沟通提前,从而将反馈周期缩短,减少返工概率。另外,契约一旦提交,自动生成的 server 端和 client 端 sdk 也同时可用,消除了开发过程中消费方和服务方之间的依赖,两端可以并行开发,减少等待时间,提升开发效率。

  如果在开发的过程中,哪一方再出现细节问题,需要调整契约时,可以尽早找到另一方进行讨论。此时由于双方都已经进入开发,都了解一些相应的细节,讨论内容更加具体,更加高效,而且讨论产生的契约变动也会更早产生效果。

  契约先行的适用场景

  契约先行开发并非银弹,它在解决特定场景下的问题时,才更“划得来”。

  比如契约应简单直接。 一些非空校验,格式要求,简单的字段间匹配,使用契约先行和生成代码都是低投入高回报,生成的代码具有非常好的约束性。但是如果契约中包含了丰富的业务逻辑,不容易在单个 OpenAPI doc 中描述的,还是手写契约测试更加明晰,维护性也更好。

  再比如我们使用的编程语言或框架需要得到 OpenAPI generator 良好的支持。 如果不能根据契约生成好用的代码,或者在生成代码的过程中需要做过多的定制化,那么该方法可能并不适用,或者并不划算。

  在开发过程中需要有健全的集成测试或者组件测试。 上述生成的代码中,虽然 sdk 可以对服务间的通信进行合法性约束,但是很多单元测试并不能提前发现问题。比如在 server 端,我们生成的 @NotNull 等注解,需要把 Spring 启动,且测试用例对业务逻辑具有足够的覆盖,才能及早发现问题,避免线上报错。

  契约先行的成本

  天下没有免费的午餐,在带来上述优势的同时,契约先行开发也会带来一些成本。

  最主要的成本是 OpenAPI generator 的学习成本。目前 OpenAPI generator 虽然已经可以支撑大多数的语言和框架,但是要做到足够好用,还需要对生成代码进行一些定制化,这些定制化需要一些时间投入。

  结论

  在服务间合作开发的过程中,为了维护契约的有效性,适用契约测试可以让不同团队之间的开发在一定程度上解耦。在一定场景下,使用契约先行的合作方式可能更高效,比如契约足够简单直接,开发使用的技术适用于生成的代码,开发过程中已经有足够的集成测试或组件测试时,契约先行可以缩短团队间的反馈闭环,减少等待时间,提升开发效率。


作者:Thoughtworks洞见    

来源:http://www.51testing.com/html/80/n-7795080.html

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   摘要:写代码就像建房子,一砖一瓦大家都知道,难点是在你如何盖的更高级。对于写代码来说,基础语法无非就那几个,很多人都会,关键是如何将代码写得健壮、写得条理清楚。  在学习自动化开发语言过程中,很多人都会有一种感觉,就是自己好像什么都会一些,但是会的都是基础知识点,还不够”深“、不够高级,面试时不敢大胆的说:“我会!我懂!”  特别是看别人写好的代码时发现,有好多个文件、方法,调来调去的、跳来跳去的,贼崇拜,大佬!大神级别的存在呀!  但是看看自己只会写单个接口、单个方法,看起来很low,完全没自信出去面试,也不敢开高薪。  最近自己发表了一个文章,就是把自己学过的知识点由一锅大骨汤,浓缩成...
            14 14 1664
            分享
          •   一直以来,在整个IT行业中,一说起软件测试这个工作,人们脑子中浮现的都是一群软件测试工程师用双手在键盘上或在手机上“点点点”的场景,所以很长一段时间,软件测试工程师都被戏称为“点点点”工程师。不过,现在0202年都过了这么久了,如果还抱着这种态度来看待“软件测试”这个职业,未免有点太过时了。这就跟前几年台湾人民觉得祖国大陆人民吃不起茶叶蛋一样,“Out的妈妈给Out开门,Out到家了”。  况且,现在的软件测试跟传统的软件测试相比已经发生了非常大的变化,不管是测试范围还是测试手段,都有很大的不同。所以,现在我们更专业的叫法是“测试开发工程师”,从这个名字就可以看出来,传统的点点点已经没有市...
            0 0 936
            分享
          • 前言Jmeter是一款用于测试客户端/服务端结构的软件,不仅可以用来测试静态和动态资源的性能。Jmeter还可用于模拟大量负载来测试一台服务器,网络或者对象的健壮性或分析不同负载下的整体性能。今天我就来教大家如何用Jmeter来实现简单的登录测试。第一步:添加线程组在测试计划上右击-添加-Threads–线程组线程数:登录用户人数Ramp-up Period:5s内5个人完成登录,隔一秒就开始登录下一个人循环测试:可以设定请求次数,此处是5个人循环两次,发送10个http请求。第二步:添加http请求默认值在线程组上右击-添加-配置元器件-http请求默认值这是下边sampler相同信息的集合...
            11 11 2856
            分享
          • 提起自动化测试,可谓仁者见人,智者见智,心中五味杂陈啊!你从任何一个招聘渠道来看最近两年对测试岗位的要求,几乎都要求会自动化测试。而不少人一直认为手工测试才是王道,工作中有的时候也用不到程序,干嘛在面试的时候要求写代码呢?明明自己的测试能力不错,做了多年的功能测试,为什么面试的时候四处碰壁呢?很是想不明白,难道业界最近几年有点儿盲目推崇自动化测试了吗?虽然我不能说完全明白这些问题的产生的根源,不过以我这五六年的从事测试工作的经验来分析一下这些问题的来龙去脉,可能也不是十分准确,希望能给迷茫的同学带来一些思考。一、 什么是自动化测试什么是自动化测试呢?关于这个问题,可能没有准确的答案。...
            0 0 700
            分享
          • 我们平时说的H5测试就是HTML5,其实就是:移动端WEB页面。我们以往的APP是使用原生系统内核的,相当于直接在系统上操作,是我们传统意义上的软件,更加稳定;H5的APP先得调用系统的浏览器内核,相当于是在网页中进行操作,较原生APP稳定性稍差,似乎还没有百万级用户量的H5APP;H5最大的优点是可以跨平台,开发容易,APP的话需要用ANDROID的语言和IOS的语言各自写,H5只要开发一套;简单的说:H5是基于web,native基于客户端。一、如何判断一个APP页面是否是H5页面1、无网络断开网络,显示404或则错误页面的是H5‘2、页面布局在手机设置、开发者选项中开启显示布局边界功能;...
            13 14 1422
            分享
      • 51testing软件测试圈微信