契约测试?生产者?消费者?一文帮你理清楚

news2024/11/25 16:27:21

契约测试?生产者?消费者?一文帮你理清楚

    • 契约测试
      • 什么是契约?
    • 先从测试金字塔讲起
    • 什么是锲约测试?
      • 但它们也有一些令人痛苦的缺点。:
      • 通常具有与 e2e 集成测试相反的属性:
    • 那么,什么是微服务架构?
    • 基于契约的测试。生产者和消费者
    • 契约测试是如何进行的?
    • 我们举一个例子
    • 以Pact框架为一个例子
    • 总结

在这里插入图片描述
五星上将麦克阿瑟曾经说过“在契约测试面前,集成测试就是个弟弟“

契约测试

什么是契约?

如果从契约产生的阶段来说,现有资料表明最早要追溯到西周时期的《周恭王三年裘卫典田契》,将契约文字刻写在器皿上,就是为了使契文中规定的内容得到多方承认、信守,“万年永宝用”。所以订立契约的本身,就是为了要信守,就是对诚信关系的一种确立。诚信,是我国所固有的一种优良传统,也是延续了几千年的一种民族美德,在中国儒家的思想体系里,是伦理道德内容中的一部分。

然而,现在不是这么美好,现实中缺少契约精神的比比皆是
在这里插入图片描述
但是,在软件测试领域,契约这把利器,又重新的利用起来

先从测试金字塔讲起

在这里插入图片描述
对于测试而言,这个金字塔是理解测试级别的最好的隐喻,这个金字塔最早出于

Mike Cohn 在他的《Succeeding with Agile》,我们从底层往上读

  1. 单元测试通常是添加到项目中最常见的测试。目标是在函数或方法级别验证代码。如果您有 sum 函数,那么您想要检查它5 + 5 = 10。通常编写和维护此类测试很容易。

/**
 * the function to test
 */

const sum = (a, b) => {
  return a + b;
};


/**
 * the unit test
 */

test("adds 5 + 5 to equal 10", () => {
  expect(sum(5, 5)).toBe(10);
});

  1. 集成测试(或系统测试)检查组件之间的接口。您可以测试整个类或服务,这通常涉及mock模拟无法在测试环境中重现的外部接口。编写集成测试有点困难,因为涉及的代码更多,而且维护成本也更高。一次测试大量代码,因此追踪问题可能需要一些时间。
    在这里插入图片描述
  2. 端到端(E2E)测试是最完整的测试,因为目标是模拟产品的最终用户。您通常需要构建一个完整的端到端环境,其中包含应用程序的所有组件(所有服务、后端存储等)。您可以使用 Postman 等工具来模拟 REST 调用,或使用 Cypress 等工具来模拟通过 Web 应用程序界面的使用情况。通常,您将编写较少的 E2E 测试,因为它们在运行时间和维护时间方面都花费大量时间。

什么是锲约测试?

但是,显而易见的出现了一个问题

虽然金字塔顶部的测试更能代表客户的体验,

但它们也有一些令人痛苦的缺点。:

很慢;由于它们遍历多个系统并且通常必须串行运行,因此每个测试可能需要几秒钟到几分钟才能完成,特别是在必须执行先决设置(例如数据准备)的情况下。

难以维护;端到端测试要求所有系统在运行之前都处于正确的状态,包括正确的版本和数据。

可能不可靠或不稳定:由于编排测试环境的复杂性,它们经常会失败,导致误报,从而分散团队的注意力。在许多情况下,它们会由于与任何代码更改无关的配置问题而失败。

难以修复:当端到端测试失败时,由于问题的分布式和远程性质,调试问题通常很困难。

规模严重;随着越来越多的团队的代码得到测试,事情变得更加复杂,测试套件的运行速度呈指数级下降,并且发布在自动化管道中被堵塞。

在流程中发现错误为时已晚:由于运行此类测试套件的复杂性,在许多情况下,这些测试仅在代码提交后才在 CI 上运行 - 在许多情况下,由单独的测试团队在几天后运行。这种反馈延迟对于现代敏捷交付团队来说代价极其高昂。

所以,契约测试就是为了解决这个问题

通常具有与 e2e 集成测试相反的属性:

它们运行速度很快,因为它们不需要与多个系统通信。

它们更容易维护:您不需要了解整个生态系统来编写测试。

它们很容易调试和修复,因为问题只出现在您测试的组件中 - 因此您通常会得到失败的行号或特定 API 端点。

它们是可重复的:

它们可扩展:因为每个组件都可以独立测试,所以构建管道不会随时间线性/指数增长

他们在开发人员机器上本地发现错误:合约测试可以而且应该在推送代码之前在开发人员机器上运行。
在这里插入图片描述
所以,契约测试时契约测试是一种软件测试方法,重点验证分布式架构中不同组件、服务或系统之间的交互。这种方法在多个服务或组件由不同的团队开发和维护的场景中非常有用,并且确保它们正确通信和协同工作至关重要。简而言之,契约测试是一种确保两个独立的系统(例如两个微服务)兼容并且可以相互通信的方法。

那么,什么是微服务架构?

面向微服务的架构与更传统的整体方法相反。您可以构建松散耦合的服务集合,而不是构建单个软件(例如在服务器上运行的应用程序)。微服务架构具有更小的代码库以及更好的灵活性和可扩展性等优势。
在这里插入图片描述
但微服务给测试带来了一些挑战。您可以单独测试每个服务(与集成测试一样),也可以通过端到端测试来测试整个堆栈。

不幸的是,单独测试每个服务并不能保证应用程序对用户来说能够正确运行。如果服务 A 依赖于版本 中的服务 B 的模拟1.4.0,但服务 B 正在切换到1.5.0不同的 API 实现,那么您可以在此级别中断生产而不会出现任何问题。

端到端测试需要您构建一个包含所有所需服务的完整环境,并且测试可能需要几秒或几分钟才能完成,具体取决于复杂程度。因为有很多层,所以最终可能会遇到很多问题,并且很难追踪哪些组件发生了故障。
在这里插入图片描述
这就是为什么基于契约的测试在微服务架构中如此常见。

基于契约的测试。生产者和消费者

基于契约的测试(CBT)并不是一种新的方法,但这个概念在微服务世界中很容易理解。假设您正在运行一个只有两个微服务 A 和 B 的简单系统:
在这里插入图片描述
A 正在消费服务 B。A 是消费者,B 是生产者。服务之间的对话是涉及信息交换的简单 HTTP REST 调用。

A 正在请求有关用户的信息:

GET /users/julien

B 正在提供有关用户的信息:


```python
{
    slug: "julien",
    fullname: "Julien Bras",
    twitter: "_julbrs"
}

这段对话就是一份契约。B 期望使用特定路径 ( /users/{slug}) 进行 HTTP 查询,A 期望答案为带有键slug、fullname和 的JSON 对象twitter。
在这里插入图片描述
每个测试都是简单且独立的(仅涉及一项服务),您只需测试每个关系的每一方即可。此测试同样适用于复杂的关系(例如具有多个链接服务的服务或正在使用服务的 Web UI)。

契约测试是如何进行的?

在此之前,我们先来理解一下,这三个关系

消费者(Consumer):对于调用,发起请求的一方。对于MQ,为接收消息的一方。

提供者(Provider):对于调用,响应请求的一方。对于MQ,为生成消息的一方。

契约(Contract):消费者和提供者之间的共识,是一系列交互的集合。对于HTTP调用,包括描述消费者向提供者发送什么的预期请求,以及描述消费者希望提供者返回的最小期望响应。对于消息交互,则描述消费者希望得到的最小期望消息
在这里插入图片描述
契约测试主要通过模拟服务间的交互来验证一个服务是否满足与其他服务通信的“契约”。

首先,每一个服务都需要为其外部通信定义一个契约。这个契约包含了服务端需满足的请求格式和预期的响应格式。例如,如果一个服务接受特定的HTTP请求并回应JSON格式的数据,那么这个请求的URL、方法(POST, GET等)、可能包含的请求头、可能的请求体中的字段,并且定义了对应的响应码、响应头以及响应体的内容,所有这些都会在契约中进行定义。

当定义好契约后,就可以进行契约测试了。契约测试主要包括以下两个步骤。

提供者端的契约测试:提供者端的契约测试主要是检查服务是否能够按照契约的规定,正确的处理请求并返回预期的响应。在这个过程中,测试框架会模拟各种请求,然后与契约中定义的响应进行对比,看这个服务是否满足契约。如果任何一个测试请求的响应与契约中定义的响应不符, 所有的契约测试就会失败,并进一步指出不一致的地方。

消费者端的契约测试:消费者端的契约测试主要是检查服务是否能够正确的发出契约中定义的请求,并正确处理预期的响应。在这个过程中,测试框架会模拟服务端,根据契约的定义返回预设的响应,看看消费者是否能够正确处理。如果消费者没能按照契约正确处理这些响应,那么测试也会失败。

对于消费者和提供者的测试,通常会采用一些流行的契约测试工具,例如Pact, Spring Cloud Contract等。

使用这种方式,契约测试可以保证服务间的交互都是符合预期的,而不论系统是否已经部署或者处于什么样的状态,它都只关注单个的服务或者连接,而忽略了系统的其它部分。这使得我们可以在系统的初期就验证服务间的交互是否正确,避免了在部署或者系统运行期间才发现问题,提高了开发和部署时的效率和可靠性。

在这里插入图片描述

我们举一个例子

让我们假设有两个服务:订单服务(Provider)和库存服务(Consumer)。库存服务的角色是在收到订单请求时减少相应的物品数量。这两个服务之间的交互会通过HTTP API进行。

在这个场景中,我们定义的“契约”能够是以下形式:当订单服务向库存服务发送一个POST请求,这个请求包含订单详情(例如,产品ID和数量),如:


POST /inventory/update
Content-Type: application/json
{
    "productId": "123",
    "quantity": 3
}

库存服务则需要返回一个200状态码,并确认减少的数量,如:

200 OK
Content-Type: application/json
{
    "productId": "123",
    "quantity": 3,
    "status": "success"
}

在这个契约定义好之后,我们就可以进行契约测试了。

在生产者(订单服务)端的契约测试,我们会模拟库存服务发送的请求,然后检查订单服务的响应是否满足契约。比如我们会构建一个请求,包含productId为"123",quantity为3,然后检查返回的响应是否是200状态码,返回的JSON是否包含productId为"123",quantity为3以及status为"success"。

在消费者(库存服务)端的契约测试,我们会模拟订单服务,发送一个包含productId为"123",quantity为3的响应,然后看库存服务是否能够正确处理这个响应。例如,库存服务需要在接收到这个响应后,减少ID为"123"的商品的库存数量3。

以Pact框架为一个例子

以下是订单服务(Provider)的契约测试样例:


from pact import Consumer, Provider
from requests.api import post

# 创建一个Pact对象。Consumer是库存服务,Provider是订单服务。
pact = Consumer('InventoryService').has_pact_with(Provider('OrderService'))

# 定义交互
pact.start_service()
pact.given(
    'A request from InventoryService for order update'
).upon_receiving(
    'A POST request for order update'
).with_request(
    method='POST',
    path='/inventory/update',
    body={
        'productId': '123',
        'quantity': 3
    }
).will_respond_with(
    status=200,
    body={
        'productId': '123',
        'quantity': 3,
        'status': 'success'
    }
)

# 契约测试
with pact:
    result = post(pact.uri, json={'productId': '123', 'quantity': 3})

# 检查结果
assert result.json() == {'productId': '123', 'quantity': 3, 'status': 'success'}
pact.stop_service()

在上面的代码中,我们首先定义了Consumer(库存服务)跟Provider(订单服务)之间的契约。然后我们开始了Provider的模拟服务,并定义了一个交互,这个交互定义了库存服务发来的请求如何以及订单服务的响应应该是什么。最后,我们在Pact的上下文管理器中执行契约测试,发送请求并检查响应是否符合预期。如果所有检查都通过,那么我们就可以确认订单服务满足了与库存服务之间的契约。否则,我们就需要修复订单服务以满足契约。

那么,这个例子中,订单服务是如何处理库存服务发来的请求的?

通常在实际场景中的微服务体系中,订单服务会有专门的路由和处理函数来处理库存服务发来的请求。假设我们使用Flask框架并展示一个简单地处理POST请求的例子


from flask import Flask, request, jsonify

app = Flask(__name__)

# 这个字典用来存储商品的库存信息
inventory = {"123": 10}

@app.route("/inventory/update", methods=["POST"])
def update_inventory():
    # 获取请求的JSON数据
    data = request.get_json()

    # 获取商品ID和需要更新的数量
    product_id = data["productId"]
    quantity = data["quantity"]

    # 更新商品的库存信息
    inventory[product_id] -= quantity

    # 返回响应
    return jsonify({
        "productId": product_id,
        "quantity": quantity,
        "status": "success"
    })

if __name__ == "__main__":
    app.run()

在以上代码中,我定义了一个路由"/inventory/update",这个路由只接受POST请求。当订单服务接收到库存服务的请求时,会执行update_inventory函数。这个函数首先会解析请求的JSON数据获得商品的ID和需要更新的数量,然后更新库存信息。最后,返回一个包含更新后的信息的JSON数据作为响应。这就是一种可能的订单服务处理函数的实现方式。

总结

契约测试和其他测试的对比
在这里插入图片描述
如果您正在管理微服务应用程序,CBT 可以成为您的测试武器库的一个很好的补充。如果使用得当,它可以取代现有E2E测试的重要组成部分。

微信公众号搜索【一个正经的测试】,专注于AI与软件测试技术和宝藏干货分享,每天准时更新原创技术文章,每月不定期赠送技术书籍,让我们在测试会所在测试社区这个大家庭一起学习交流。喜欢记得星标⭐我,每天及时获得最新推送,
在这里插入图片描述
后台回复“软件测试基础”、“AI与大模型“,简历与面试”等领取测试资源,回复“微信交流群”、“内推群”一起进群吹水摸鱼。
个人微信llwfancymyself添加请注明来意 😃

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

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

相关文章

删除文件怎么找回?恢复文件,3个实用方法!

“我经常会在操作电脑时误删重要的文件,有什么方法可以恢复删除的文件吗?希望大家给我推荐几个有效的操作方法!” 在日常生活和工作中,我们经常会遇到误删文件的情况,这时如何快速有效地恢复这些文件就显得尤为重要。删…

Eclipse搭建python环境

一、下载eclipse eclipse官网下载参考链接 二、 下载PyDev ​PyDev 三、安装和配置pyDev 下载完PyDev,解压之后是下面两个文件夹,我下载的版本是PyDev 7.7 ,然后拷到eclipse对应的目录下就可以 四、然后新建一个python程序 1.新建一个项目 ​​…

S/MIME电子邮件证书申请指南

近年来,邮件安全问题日益突出,电子邮件成为诈骗、勒索软件攻击的重灾区。恶意邮件的占比屡创新高,邮件泄密事件更是比比皆是。在如此严峻的网络安全形势下,使用S/MIME电子邮件证书进行邮件收发是当今最佳的邮件安全解决方案之一。…

设计模式的学习笔记

设计模式的学习笔记 一. 设计模式相关内容介绍 1 设计模式概述 1.1 软件设计模式的产生背景 设计模式最初并不是出现在软件设计中,而是被用于建筑领域的设计中。 1977 年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任 Christopher Alexander 在…

SD-WAN网络建设:设备、服务与综合解决方案

随着数字化转型的推进,企业对于网络性能和连接的需求日益增加,而SD-WAN(软件定义广域网)作为一项创新的网络解决方案,成为提升效率和灵活性的关键工具。然而,构建一个完善的SD-WAN网络并非仅仅关乎设备的采…

橘子学K8S04之重新认识Docker容器

我们之前分别从 Linux Namespace 的隔离能力、Linux Cgroups 的限制能力,以及基于 rootfs 的文件系统三个角度来理解了一下关于容器的核心实现原理。 这里一定注意说的是Linux环境,因为Linux Docker (namespaces cgroups rootfs) ! Docker on Mac (bas…

(2023版)斯坦福CS231n学习笔记:DL与CV教程 (4) | 神经网络与反向传播

前言 📚 笔记专栏:斯坦福CS231N:面向视觉识别的卷积神经网络(23)🔗 课程链接:https://www.bilibili.com/video/BV1xV411R7i5💻 CS231n: 深度学习计算机视觉(2017&#xf…

storm统计服务开启zookeeper、kafka 、Storm(sasl认证)

部署storm统计服务开启zookeeper、kafka 、Storm(sasl认证) 当前测试验证结果: 单独配置zookeeper 支持acl 设置用户和密码,在storm不修改代码情况下和kafka支持当kafka 开启ACL时,storm 和ccod模块不清楚配置用户和密…

提升UI设计水平的关键技能,轻松打造专业形象!

UI关注软件图形界面,包括按钮、布局、动画、过渡、微交互等。简而言之,UI关于界面的外观。 UI设计包括以下界面: -用户图形界面(GUI):GUI设计用户与系统控制工具之间的视觉交互。计算机桌面是一种GUI。 -语音控制界面(VUI):VUI设计用户与系…

网站SEO优化方案

1,去各类搜索引擎里面,注册你的站点 解决方案:注册地址:https://seo.chinaz.com/chinaz.com 2,网站地址使用 https 会增加搜索排名 解决方案:https:www.xxx.com 3,官网每个页面的 meta 里面&a…

小明和完美序列——map

由完美序列的定义可以知道,序列中每个数字的个数,要么是等于它本身,要么就是一个没有。 如果一个数字出现的次数比它本身大,我们就选择删掉多出的, 这样肯定比完全删除是更优的,只有在当一个数字出现的次数…

宏集干货丨探索物联网HMI的端口转发和NAT功能

来源:宏集科技 工业物联网 宏集干货丨探索物联网HMI的端口转发和NAT功能 原文链接:https://mp.weixin.qq.com/s/zF2OqkiGnIME6sov55cGTQ 欢迎关注虹科,为您提供最新资讯! #工业自动化 #工业物联网 #HMI 前 言 端口转发和NAT功…

数据结构和算法的部分例题(力扣)

1.数组 1.1 合并一个数组的两个有序区间 public class MargTwo {public static void main(String[] args) {int[] arr1{1,5,6,2,4,10,11};int[] arr2new int[arr1.length];marg2(arr1,0,2,3,6,arr2);}private static void marg2(int[]arr1,int iStar,int iEnd,int jStar,int j…

百度搜索Push个性化:新的突破

作者 | 通用搜索产品研发组 导读 本文简单介绍了百度搜索Push个性化的发展过程,揭示了面临的困境和挑战:如何筛选优质物料、如何对用户精准推荐等。我们实施了一系列策略方法进行突破,提出核心的解决思路和切实可行的落地方案。提升了搜索DAU…

OceanBase集群部署

我认为学习一个中间件比较好的方式是,先了解它的架构和运行原理,然后动手部署一遍,加深对它的了解,再使用它,最后进行总结和分享 本篇介绍OceanBase部署前提条件和集群部署 1.使用开源免费的社区版,企业版…

配置redis挂载

1. 暂停和删除redis 2.创建文件夹 /usr/local/software/redis/6379/conf/ /usr/local/software/redis/6379/data/ 把redis-conf文件上传到conf文件夹中 3.配置网络 docker network create --driver bridge --subnet172.18.12.0/16 --gateway172.18.1.1 wn_docker_net 4.运…

vue的简单认识

vue是一套前段框架,免除了原生JavaScript中的dom的繁杂操作,简化书写。 vue基于MVVM(Model-View-ViewModel)思想,实现数据的双向绑定,将编程的重点放在数据上。 简单说就是,我们会把操作数据库…

k8s---ingress对外服务(七层)

ingress 概念 k8s的对外服务,ingress service作用现在两个方面: 1、集群内部:不断跟踪的变化,更新endpoint中的pod对象,基于pod的ip地址不断变化的一种服务发现机制。 2、集群外部:类似于负载均衡器&a…

一文了解Servlet

文章目录 1、什么是Servlet2、Servlet快速入门3、Servlet生命周期4、Servlet体系结构5、urlPatern配置6、XML编写Servlet 1、什么是Servlet Servlet是Java提供的一门动态web资源开发技术Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Serv…

STM32F103标准外设库——RCC时钟(六)

个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755qq.com 🦉个人WeChat:V…