当前位置:首页 > 科技  > 软件

探秘Spring Contract:如何保障您的API符合预期?

来源: 责编: 时间:2024-03-22 17:42:29 124观看
导读微服务集成测试之痛环境搭建成本高,需启动多个服务用例编写难运行慢发现问题晚测试脆弱,外部依赖多1.什么是契约测试契约测试(Contract testing)是一种测试技术,它通过以隔离检查集成点上的每个应用的方式,确保应用发送或接

CRM28资讯网——每日最新资讯28at.com

微服务集成测试之痛

  • 环境搭建成本高,需启动多个服务
  • 用例编写难
  • 运行慢
  • 发现问题晚
  • 测试脆弱,外部依赖多

1.什么是契约测试

契约测试(Contract testing)是一种测试技术,它通过以隔离检查集成点上的每个应用的方式,确保应用发送或接收的消息符合调用双方共识,并允许随着时间的推移进行演化。CRM28资讯网——每日最新资讯28at.com

契约测试是对单元测试的增强,针对服务接口provider测试,覆盖了一部分本来需要集成测试才能测试到的场景。CRM28资讯网——每日最新资讯28at.com

2.为什么要做契约测试

契约测试主要解决在存在沟通边界情况下,测试替身(Test Double)与生产代码表现可能不一致的问题。在契约测试中,契约由代码生成,保持与现实同步,而且应用可以独立于其它应用而仅基于契约进行快速测试。CRM28资讯网——每日最新资讯28at.com

由于集成测试容易受到网络缓慢或不可靠,以及服务不可靠等因素的影响而运行缓慢或失败,所以通常会引入测试替身来代替真实外部服务,以快速完成覆盖度更广的测试,让测试真正起到作用。CRM28资讯网——每日最新资讯28at.com

3.契约测试的定位

金字塔模型是构建健康、快速、可维护测试集的成熟理论。CRM28资讯网——每日最新资讯28at.com


CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com


CRM28资讯网——每日最新资讯28at.com

4.契约测试的价值

众所周知,越是在项目生命周期的后期发现Bug,其修复的成本就越高。CRM28资讯网——每日最新资讯28at.com


CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com


CRM28资讯网——每日最新资讯28at.com

不同于端到端(E2E)测试,契约测试可以在开发人员推送代码之前运行,在开发阶段提早发现问题。CRM28资讯网——每日最新资讯28at.com

契约测试还有很多端到端测试不具备的好处:CRM28资讯网——每日最新资讯28at.com

  • 不需要调用其它组件,运行得很快。
  • 编写测试不需要了解系统全貌,更容易维护。
  • 问题只存在于被测试组件中,更容易调试和修复。
  • 极易反复运行。
  • 每个组件独立测试,不会引发流水线构建时间大幅增长。

引入契约测试,还会带来如下福利:CRM28资讯网——每日最新资讯28at.com

  • 在提供者API就绪之前就可以开发消费者应用。
  • 为提供者供应准确的需求
  • 会收获一组文档化良好的用例,它们确切地显示了如何使用提供者。
  • 提供者对API变更更有信息,可以准确知道使用者感兴趣的字段,方便地移除未使用的字段,以及添加新的字段。
  • 对提供者API进行修改,可以立即看到会影响哪些使用者。

没有两个团队是完全一样的,契约测试也不是万能的,关键要看契约测试可以为团队和项目带来什么。CRM28资讯网——每日最新资讯28at.com

5.契约测试适合的场景

契约测试可以用于任何需要通信的两个服务,比如Web前端与后端API服务。CRM28资讯网——每日最新资讯28at.com

在微服务架构体系中,因为存在更多团队独立、服务间调用及服务单独演进的情形,契约测试有了更好更大的用武之地。良好的契约测试,使得开发人员很容易避免版本地狱,是微服务开发和部署的利器。CRM28资讯网——每日最新资讯28at.com

6.概念术语

契约测试主要涉及如下概念术语:CRM28资讯网——每日最新资讯28at.com

  • 消费者(Consumer):对于调用,发起请求的一方。对于MQ,为接收消息的一方。
  • 提供者(Provider):对于调用,响应请求的一方。对于MQ,为生成消息的一方。
  • 契约(Contract):消费者和提供者之间的共识,是一系列交互的集合。对于HTTP调用,包括描述消费者向提供者发送什么的预期请求,以及描述消费者希望提供者返回的最小期望响应。对于消息交互,则描述消费者希望得到的最小期望消息。


CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

7.契约测试模式

契约测试分为消费者驱动(consumer-driven)和提供者驱动(Provider-driven)两种模式。CRM28资讯网——每日最新资讯28at.com

消费者驱动更具哲学意义,将API的消费者置于设计过程的核心,来倡导更好的内部微服务设计。该模式的优点在于,只有消费者正在使用的部分会得到测试,而提供者可以自由地更改消费者不使用的任何其它部分,而不必破坏任何现有测试。CRM28资讯网——每日最新资讯28at.com

提供者驱动思路较为常规,更适合开放数据或系统的场景。CRM28资讯网——每日最新资讯28at.com

无论采用哪种风格,关键在于获得契约测试的好处,实现引入契约测试的目的。CRM28资讯网——每日最新资讯28at.com

(1)消费者驱动

消费者驱动的契约测试运行步骤如下:CRM28资讯网——每日最新资讯28at.com

  • 步骤1:消费者端
  • 编写并运行单元测试(包括对接口的请求参数和预期响应)
  • MockService代替实际服务提供者(自动)
  • 生成契约文件(自动)
  • 步骤2:提供者端
  • 启动服务提供者
  • 重放契约文件中的请求,验证真实响应是否满足预期(自动)


CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

(2)提供者驱动

提供者驱动模式由提供者定义契约并驱动整个过程。CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

8.契约测试工具

流行的契约测试工具为:CRM28资讯网——每日最新资讯28at.com

  • Pact:是一个命令行工具,反馈时间更短,有助于消费者和生产者之间更好地沟通,支持与Maven/Gradle等集成。
  • Spring Cloud Contract:主要用于JVM环境,也容易扩展到非JVM环境,主要适用于生产者驱动的契约测试。

9.利用Pact进行消费者驱动的测试价值CRM28资讯网——每日最新资讯28at.com

利用Pact进行契约测试的整个流程示意如下,使用了 pact 之后,依然是每个服务独立的进行单元测试,但是可以模拟出真实集成场景。CRM28资讯网——每日最新资讯28at.com

  • 将一个笨重的集成测试化为两个容易编写、容易运行的单元测试/接口测试
  • 解耦消费者与提供者,甚至可以在没有提供者实现的情况下开展消费者端测试
  • 通过测试保证契约和实现的一致性,测试通过之时就是代码实现完成之时
  • 测试前移:在开发阶段就应该运行,并作为CI的一部分,便于尽早发现问题


CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

10.Pact示例

pact契约测试分为两步:CRM28资讯网——每日最新资讯28at.com

  • 编写test用例,生成契约文件(不需要启动服务)。
  • 利用pact-verifier命令和契约文件,验证接口提供者是否正确 (需要启动提供者服务)

以下为nlp-pact-parent示例:CRM28资讯网——每日最新资讯28at.com

(1)父项目pom包相关

<dependencies><!-- contract testing --><dependency>    <groupId>au.com.dius</groupId>    <artifactId>pact-jvm-consumer-junit5</artifactId>    <version>4.0.10</version>    <scope>test</scope></dependency><dependency>    <groupId>au.com.dius</groupId>    <artifactId>pact-jvm-provider-junit5</artifactId>    <version>4.0.10</version>    <scope>test</scope></dependency>

(2)nlp-pact-consumer消费端项目

编写ConsumerTest生成契约:CRM28资讯网——每日最新资讯28at.com

@ExtendWith(PactConsumerTestExt.class)@SpringBootTest(classes = PactConsumerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)@PactTestFor(providerName = "nlp-pact-provider", port = "8202")public class ConsumerTest {   private TestRestTemplate restTemplate = new TestRestTemplate();   @Test   @PactTestFor(pactMethod = "greetingPact")    void greeting_shouldReturnMessage() {      // Arrange      HttpHeaders headers = new HttpHeaders();      headers.add("Content-Type", "application/json");      // Act      ResponseEntity<Map> response = restTemplate.getForEntity("http://localhost:8202/greeting?name=John", Map.class);      // Assert      assertEquals(HttpStatus.OK, response.getStatusCode());      assertEquals(Collections.singletonMap("message", "Hello, John!"), response.getBody());   }   // Pact 定义   @Pact(consumer = "nlp-pact-consumer", provider = "nlp-pact-provider")   public RequestResponsePact greetingPact(PactDslWithProvider builder) {      return builder            .given("a request for greeting with name 'John'")            .uponReceiving("a request to greet John")            .path("/greeting")            .method("GET")            .query("name=John")            .willRespondWith()            .status(200)            .headers(Collections.singletonMap("Content-Type", "application/json"))            .body("{/"message/": /"Hello, John!/"}")            .toPact();   }}

(3)执行ConsumerTest测试用例,生成如下契约文件:

# nlp-pact-consumer-nlp-pact-provider.json{  "provider": {    "name": "nlp-pact-provider"  },  "consumer": {    "name": "nlp-pact-consumer"  },  "interactions": [    {      "description": "a request to greet John",      "request": {        "method": "GET",        "path": "/greeting",        "query": {          "name": [            "John"          ]        }      },      "response": {        "status": 200,        "headers": {          "Content-Type": "application/json"        },        "body": {          "message": "Hello, John!"        }      },      "providerStates": [        {          "name": "a request for greeting with name 'John'"        }      ]    }  ],  "metadata": {    "pactSpecification": {      "version": "3.0.0"    },    "pact-jvm": {      "version": "4.0.10"    }  }}

(4)nlp-pact-provider提供端验证契约

build配置如下:CRM28资讯网——每日最新资讯28at.com

<plugin>    <groupId>au.com.dius</groupId>    <artifactId>pact-jvm-provider-maven</artifactId>    <version>4.0.0</version>    <configuration>        <serviceProviders>            <!-- You can define as many as you need, but each must have a unique name -->            <serviceProvider>                <name>nlp-pact-provider</name>                <!-- All the provider properties are optional, and have sensible defaults (shown below) -->                <protocol>http</protocol>                <host>localhost</host>                <port>8200</port>                <path>/</path>                <pactFileDirectory>resources/pacts</pactFileDirectory>            </serviceProvider>        </serviceProviders>        <pactBrokerUrl/>    </configuration></plugin>

(5)运行命令:mvn pact:verify,验证契约

Found 1 pact filesVerifying a pact between nlp-pact-consumer and nlp-pact-provider  [Using File D:/IdeaProjects/nlp-other-project-dev/nlp-pact-parent/nlp-pact-provider/target/pacts/nlp-pact-consumer-nlp-pact-provider.json]  Given a request for greeting with name 'John'         WARNING: State Change ignored as there is no stateChange URL  a request to greet John    returns a response which      has status code 200 (OK)      has a matching body (OK)[WARNING] Skipping publishing of verification results as it has been disabled (pact.verifier.publishResults is not 'true')[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time:  1.589 s[INFO] Finished at: 2023-03-08T15:02:33+08:00[INFO] --------

11.Pact Broker

Pact Broker是一个用于共享消费者驱动的合同和验证结果的应用程序。CRM28资讯网——每日最新资讯28at.com

pact主页面:CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

查看服务间关系:CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

与CICD集成:CRM28资讯网——每日最新资讯28at.com

CRM28资讯网——每日最新资讯28at.com

12.总结

我在不少项目中都尝试过实施契约测试,但是真正实施成功的并不多,主要原因还是规模和痛点不够大,从而导致团队觉得没有必要做,或者觉得做了收益比投入少。而成功的一般的都是团队人员足够痛,或者经历过大型多团队项目中服务改变等各种痛点,从而导致他们解决自己的痛点而主动实施契约测试,但是前提是他们都知道契约测试。所以要成功实施契约都是有两个主要的前提条件:1,团队对于相关问题足够痛,2,团队懂契约测试。在这种情况下,团队才可能愿意主动实施契约测试,才能成功的实施契约测试。所以首先是要让开发团队懂契约测试,比如契约测试能解决什么问题,实施流程,相关测试框架等,然后等待团队无法忍受相关痛点后,成功的实施契约测试就可以水到渠成了。CRM28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-78655-0.html探秘Spring Contract:如何保障您的API符合预期?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 使用 Node.js 和 htmx 构建全栈 CRUD 应用程序

下一篇: 一份关于2024年React初学者入门路线指南

标签:
  • 热门焦点
  • 红魔电竞平板评测:大屏幕硬实力

    红魔电竞平板评测:大屏幕硬实力

    前言:三年的疫情因为要上网课的原因激活了平板市场,如今网课的时代已经过去,大家的生活都恢复到了正轨,这也就意味着,真正考验平板电脑生存的环境来了。也就是面对着这种残酷的
  • Redmi Pad评测:红米充满野心的一次尝试

    Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • 这款新兴工具平台,让你的电脑效率翻倍

    这款新兴工具平台,让你的电脑效率翻倍

    随着信息技术的发展,我们获取信息的渠道越来越多,但是处理信息的效率却成为一个瓶颈。于是各种工具应运而生,都在争相解决我们的工作效率问题。今天我要给大家介绍一款效率
  • JVM优化:实战OutOfMemoryError异常

    JVM优化:实战OutOfMemoryError异常

    一、Java堆溢出堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃 圾收集回收机制清除这些对象,当这些对象所占空间超过
  • 冯提莫签约抖音公会 前“斗鱼一姐”消失在直播间

    冯提莫签约抖音公会 前“斗鱼一姐”消失在直播间

    来源:直播观察提起&ldquo;冯提莫&rdquo;这个名字,很多网友或许听过,但应该不记得她是哪位主播了。其实,作为曾经的&ldquo;斗鱼一姐&rdquo;,冯提莫在游戏直播的年代影响力不输于现
  • 2纳米决战2025

    2纳米决战2025

    集微网报道 从三强争霸到四雄逐鹿,2nm的厮杀声已然隐约传来。无论是老牌劲旅台积电、三星,还是誓言重回先进制程领先地位的英特尔,甚至初成立不久的新
  • 支持aptX Lossless无损传输 iQOO TWS 1赛道版发布限时优惠价369元

    支持aptX Lossless无损传输 iQOO TWS 1赛道版发布限时优惠价369元

    2023年7月4日,“无损音质,声动人心”iQOO TWS 1正式发布,支持aptX Lossless无损传输,限时优惠价369元。iQOO TWS 1耳机率先支持端到端aptX Lossless无
  • 苹果140W USB-C充电器:采用氮化镓技术

    苹果140W USB-C充电器:采用氮化镓技术

    据10 月 30 日 9to5 Mac 消息报道,当苹果推出新的 MacBook Pro 2021 时,该公司还推出了新的 140W USB-C 充电器,附赠在 MacBook Pro 16 英寸机型的盒子里,也支
  • onebot M24巧系列一体机采用轻薄机身设计,现已在各平台开售

    onebot M24巧系列一体机采用轻薄机身设计,现已在各平台开售

    onebot M24 巧系列一体机目前已在线上线下各平台同步开售。onebot M24 巧系列采用一体化轻薄机身设计,最薄处为 10.15mm,拥有宝石红、午夜蓝、石墨绿、雅致
Top