契约开发、测试你知多少?

news2024/11/15 9:44:56

契约维护的难题

如今微服务凭借其灵活、易开发、易扩展等优势深入人心,不同服务之间的集成和交互日渐繁多且复杂。这些服务之间交互的方式是多样的,常见的有 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) 的实践。

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

契约测试驱动的合作流程

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

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

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

契约测试有时修改代价高

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

更重要的是,随着测试的编写,生成的契约可能比当时商讨的更为简单,比如一些 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 进入开发。

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

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

契约先行的适用场景

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

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

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

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

契约先行的成本

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

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

结论

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

 最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mysql架构与内部模块

Mysql架构与内部模块 演示环境: MySQL 5.7 存储引擎:InnoDB 一、一条查询SQL是如何执行的? 程序或者工具要操作数据库,第一步跟数据库建立连接。 1、通信协议 首先,MySQL 必须要运行一个服务,监听默认的…

vue - vue中对Vant日历组件(calendar)的二次封装

vue中对vant日历选择器组件实现的的二次封装;主要实现功能如下: 主要功能: 日期区间选择(基本);自定义选择器的底部按钮,添加清除时间操作(slot插槽);指定默认选中的日期…

一种图片展示的完美方案,图片展示,object-fill

通常一般的处理 <style>.img-container {width: 300px;height: 200px;background: #f60;}img {width: 100%;height: 100%;}</style> </head> <body><div class"img-container"><img src"./行道树.png" alt""&g…

Java之JVM性能初探

一、JVM简介jvm是一种用于计算设备的规范&#xff0c;它是一个虚构出来的机器&#xff0c;是通过在实际的计算机上仿真模拟各种功能实现的。jvm包含一套字节码指令集&#xff0c;一组寄存器&#xff0c;一个栈&#xff0c;一个垃圾回收堆和一个存储方法域。JVM屏蔽了与具体操作…

AWS攻略——使用Public NAT解决私有子网实例访问互联网

文章目录创建NAT网关编辑Private子网路由测试知识点参考资料在《AWS攻略——子网》一文中&#xff0c;我们分别创建了一个Public子网和一个Private子网&#xff0c;并让Public子网中的实例可以SSH登录到Private子网的实例中。 现实场景中&#xff0c;我们可能存在如下需求&…

正版授权的商业级智慧校园源码 SaaS模式带小程序端

一套针对中小学校园研发的商业级智慧校园源码&#xff0c;系统功能强大&#xff0c;代码完整&#xff0c;源码有演&#xff0c;正版授权。 私信了解更多&#xff01; 一、智慧校园系统构成&#xff1a; 1、 SaaS云平台 2、 智慧校园管理平台 3、 小程序教师端 4、 小程序家…

【一文带你看懂什么是VLAN、网关、DNS和子网掩码等 】

很多小伙伴多次问到什么是VLAN、三层交换机、网关、MAC地址、DNS和子网掩码&#xff0c;它们具体的定位和用途。确实&#xff0c;如今网络技术已经覆盖了非常广阔的工作和生活场景&#xff0c;但很多人在日常的应用当中还是不太懂这些知识&#xff0c;今天我们就尝试用比较通俗…

从功能测试进阶自动化测试涨薪7k,终究是逼了自己一把...

绝大多数测试工程师都是从功能测试做起的&#xff0c;工作忙忙碌碌&#xff0c;每天在各种业务需求学习和点点中度过&#xff0c;过了好多年发现自己还只是一个功能测试工程师随着移动互联网的发展&#xff0c;从业人员能力的整体进步&#xff0c;软件测试需要具备的能力要求越…

Retrofit+Hilt后端请求小项目1--项目介绍

简介 本项目根据 youtube 对应教程实现而来 将会对对应代码以及依赖&#xff08;如 Hilt、retrofit、coil&#xff09;进行详细的分析与解读&#xff0c;同时缕清项目结构安排 如文章有叙述不清晰的&#xff0c;请直接查看原教程&#xff1a;https://www.youtube.com/watch?…

vs2019调用matlab2021混合编程

vs2019调用matlab2021 调试程序时使用vs我只能输出去看最后的结果&#xff0c;想实时的观察输出&#xff0c;老师说可以用这个办法去做 主要思想 首先&#xff1a;进行环境配置&#xff0c;主要是两个方面 1. 在VS中配置MATLAB 目录&#xff1a; &#xff08;a&#xff09…

建设 TiDB 自动化平台:转转 DBA 团队实践

转转技术 . 转转研发中心及业界小伙伴们的技术学习交流平台&#xff0c;定期分享一线的实战经验及业界前沿的技术话题。 各种干货实践&#xff0c;欢迎交流分享&#xff0c;如有问题可随时联系 waterystone ~ 莫善 转转 DBA。 负责 TiDB&#xff0c;MongoDB&#xff0c;MySQL 运…

Python调用MMDetection实现AI抠图去背景

这篇文章的内容是以 《使用MMDetection进行目标检测、实例和全景分割》 为基础&#xff0c;需要安装好 MMDetection 的运行环境&#xff0c;同时完成目标检测、实例分割和全景分割的功能实践&#xff0c;之后再看下面的内容。 想要实现AI抠图去背景的需求&#xff0c;我们需要…

linux高级命令之互斥锁

互斥锁学习目标能够知道互斥锁的作用1.互斥锁的概念互斥锁: 对共享数据进行锁定&#xff0c;保证同一时刻只能有一个线程去操作。注意:互斥锁是多个线程一起去抢&#xff0c;抢到锁的线程先执行&#xff0c;没有抢到锁的线程需要等待&#xff0c;等互斥锁使用完释放后&#xff…

2023-JavaWeb最新整理面试题-TCP、Tomcat、Servlet、JSP等

Java基础面试题 一、JavaWeb专题 1.HTTP响应码有哪些 1、1xx&#xff08;临时响应&#xff09; 2、2xx&#xff08;成功&#xff09; 3、3xx&#xff08;重定向&#xff09;&#xff1a;表示要完成请求需要进一步操作 4、4xx&#xff08;错误&#xff09;&#xff1a;表示请…

MySQL锁之深入死锁分析

文章目录1 死锁产生原因分析1.1 产生原因1.2 产生示例1.2.1 案例一1.2.2 案例二1.2.3 案例三1.2.4 案例四1.2.5 案例五1.2.6 案例六1.3 死锁预防策略1.4 剖析死锁的成因1.5 解除死锁的占用1.5.1 死锁分析1.5.2 死锁解决1 死锁产生原因分析 点击此处了解MySQL各种锁分析 1.1 产…

为什么计算机需要操作系统?(一文详解~)

我们从三个方面来简单聊聊为什么计算机系统操作系统这个话题。 资源分配器 如果你的CPU上只需要运行一个程序&#xff0c;那么你的确不需要操作系统。 可是&#xff0c;一旦你的CPU上需要再运行一个程序&#xff0c;那么马上就会面临一个问题&#xff1a;两个程序开始竞争资源…

山东大学教授团畅谈ChatGPT革命座谈会,探讨ChatGPT发展趋势

2月18日&#xff0c;由山东大学多院系教授学者组成的山东大学教授团在济南福瑞达自贸创新产业园举行了“畅谈ChatGPT革命”座谈会&#xff0c;诸位教授学者就ChatGPT出现的影响进行了探讨。产业园首席顾问李铁岗教授向大家介绍产业园区山东大学经济学院教授、济南福瑞达自贸创新…

2023年美国大学生数学建模A题:受干旱影响的植物群落建模详解+模型代码(二)

前言 资源放CSDN上面过不了审核,都快结束了都没过审真的麻了,订阅专栏的同学直接加我微信直接发你。我只打造优质专栏。专注建模四年,博主参与过大大小小数十来次数学建模,理解各类模型原理以及每种模型的建模流程和各类题目分析方法。此专栏的目的就是为了让零基础快速使…

音视频基础之音频编码原理简介

一&#xff1a;隐蔽信号 数字音频信号如果不加压缩地直接进行传送&#xff0c;将会占用极大的带宽。例如&#xff0c;一套双声道数字音频若取样频率为44.1KHz&#xff0c;每样值按16bit量化&#xff0c;则其码率为&#xff1a; 244.1kHz16bit1.411Mbit/s 如此大的带宽将给信号…

linux系统编程2--网络编程socket知识

在linux系统编程中网络编程是使用socket&#xff08;套接字&#xff09;&#xff0c;socket这个词可以表示很多概念&#xff1a;在TCP/IP协议中&#xff0c;“IP地址TCP或UDP端口号”唯一标识网络通讯中的一个进程&#xff0c;“IP地址端口号”就称为socket。在TCP协议中&#…