背景信息

在软件工程领域,我们经常面临变化。“概念的到来,微服务 ”是那些最近发生的事件,它不仅改变了软件的架构,而且球队的组织方式以及它们如何协同工作之一。
以下是由M. Fowler和J. Lewis引入的微服务的定义之一:“...是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制独立部署。这些服务的集中管理最少,可以用不同的编程语言编写,并使用不同的数据存储技术。“除此之外,微服务的概念使我们能够灵活地相互独立部署服务。

在这篇博客文章中,我们想概述一下在新的微服务世界中如何测试变化。我们还将介绍消费者驱动合同测试的细节以及支持它的框架。回顾敏捷测试金字塔的众所周知的概念,当我们谈论微服务测试时,我们希望重新检查其中的想法是否仍然适用和有效。

为了更好地理解,我们将使用以下示例模型来描述微服务测试背后的概念:

在上面的图1中,我们可以看到我们有两个微服务,通过REST彼此进行通信。第一项服务扮演消费者的角色,第二项扮演提供者的角色。我们案例中的提供商或产品服务提供了所有与产品相关的信息,例如  名称,类型和说明,按给定的产品标识号码。另一方面,消费者获取数据并消费相关信息。当然可以有尽可能多的消费者。稍后,我们将向两位消费者展示一个例子,但这些应该足以建立理解。

微服务测试方法

单元测试

当我们谈论微服务时,我们是否还应该进行单元测试? - 是的,当然单元测试已经被证明是测试业务逻辑的可靠,快速且不昂贵的方法。但这里只能保证你自己服务的功能。如果您使用消费者微服务,单元测试不能保证提供者服务正常工作。

端到端(系统)测试

当我们谈论微服务时,我们是否还应该进行端到端的测试?-  是的,进行端到端测试很重要,但是当我们谈论微服务时,复杂程度可能非常高。想象一下,为了执行端到端测试,必须部署并运行五个以上的微服务。

集成测试

移动到金字塔的下一层(我们有意留下集成测试) - 我们是否应该在谈论微服务时进行集成测试?- 当然是。有几种方法可以帮助您模拟正在与之通信的微服务,例如服务虚拟化。一些提供这种功能的已知框架是WireMock,  Hoverfly。如果您有两个以上的微服务(我们可以假设这是真实世界的项目中的情况),那么在这里进行集成测试的一个缺点是它们的复杂性以及您需要测试和模拟的组合的数量。集成测试不能保证你使用的服务,它应该做的工作。为什么?我们来看一个简单的例子:
您有一个集成测试,其中提供者服务被模拟。在部署消费者服务之前,您希望证明其正常工作。首次运行时,所有测试均为绿色,然后您可以部署您的服务(请参见图2)。

但是,如果您针对生产提供商运行服务,而不是模拟版本,则会失败。在这个例子中,提供者已经改变了数据格式,并且将字段的名称从“ 名称 ”更新为“ 名称 ”。集成测试无法解决这个问题,因为它们正在针对Provider的过时版本运行(参见图3)。

你如何填补你的测试定义中的这个空白?现在介绍消费者驱动合同测试的概念

消费者驱动的合同测试(CDC测试)

消费者驱动契约方法不过是消费者和提供者之间关于它们在彼此之间转移的数据格式的协议。通常,合同的格式由消费者定义并与相应的提供商共享。之后,测试正在执行,以验证合同是否保存。CDC测试的先决条件之一是可以与提供商服务团队保持良好的最佳密切沟通(例如,当您是消费者和提供商的所有者时)。分享这些合同和交流测试结果是实施适当的CDC测试的重要部分。

协议

PACT是一个开源CDC测试框架。它还提供了广泛的语言支持,如Ruby,Java,Scala,.NET,Javascript,Swift / Objective-C。我们将通过代码示例来介绍两个主要步骤。

Github上的演示项目入门:Pact Demo Github Repo
为了遵循指南,您首先需要实现至少两个微服务。我们的演示项目基于前面介绍的产品案例。另外,我们使用Spring引导来实现微服务实现,使用Gradle作为依赖和配置管理。 在Provider中,  正在使用PACT Gradle插件(有关详细信息,请参阅  https://github.com/DiUS/pact-jvm/tree/master/pact-jvm-provider-gradle)。该指南由两个包含较小步骤的主要步骤组成。第一步着重于消费者应该做什么,第二步是对提供商方面的活动进行分组。

步骤1操作概述

1.1。定义消费者端服务的预期结果
1.2。生成PACT文件
1.3。与提供者服务共享生成的PACT文件
1.1。定义消费者端服务的预期结果

我们从实施ConsumerDemoTest开始,在那里我们指定了Provider的预期结果。有两种主要的方法,扩展基类ConsumerPactTest或使用注释。第二种方法使我们能够灵活地对每个测试类进行多个测试,并消除了继承的必要性,因此可以推荐它作为更好的测试类。在源代码中,您可以看到两个选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

public class ConsumerAnnotDemoTest {
    @Rule
    public PactProviderRule pactProviderRule = new PactProviderRule("product-provider-demo", this);
    @Pact(consumer = "product-consumer-demo")
    public PactFragment createFragment(PactDslWithProvider pactDslWithProvider) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json;charset=UTF-8");
        return pactDslWithProvider
                .given("test demo first state")
                  .uponReceiving("ConsumerDemoTest interaction")
                  .path("/product")
                  .query("id=537")
                  .method("GET")
                .willRespondWith()
                  .status(200)
                  .headers(headers)
                  .body("{" +
                        "\"name\": \"Consumer Test\"," +
                        "\"description\" : \"Consumer Test verifies provider\"," +
                        "\"type\": \"testing product\"" +
                        "}")
                .toFragment();
    }
    @Test
    @PactVerification
    public void runTest() throws Exception {
        String url = pactProviderRule.getConfig().url();
        URI productInfoUri = URI.create(String.format("%s/%s", url, "product?id=537"));
        ProductRestFetcher productRestFetcher = new ProductRestFetcher();
        Product product = productRestFetcher.fetchProductInfo(productInfoUri);
        assertEquals(EXPECTED_NAME, product.getName());
        assertEquals(EXPECTED_TYPE, product.getType());
        assertEquals(EXPECTED_DESC, product.getDescription());
    }
}

首先,我们必须为它建立请求和预期的响应。在期望部分,我们说,如果正在执行针对特定URL路径的GET请求,那么预计会出现状态为OK,标头和json正文的响应。之后,在runTest()实现中,我们针对模拟服务执行测试以设置提供程序期望值,并期望产品名称,类型和描述属性的特定值。

1.2。生成PACT文件

现在,我们来运行测试。结果如预期的那样是积极的:

如果我们打开/ target文件夹,我们将能够看到已经创建了一个带有JSON文件的新子文件夹/约定。这实际上是我们与提供商的合同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

{
    "provider": {
        "name": "product-provider-demo"
    },
    "consumer": {
        "name": "product-consumer-demo"
    },
    "interactions": [
        {
            "description": "ConsumerDemoTest interaction",
            "request": {
                "method": "GET",
                "path": "/product",
                "query": "id=537"
            },
            "response": {
                "status": 200,
                "headers": {
                    "Content-Type": "application/json;charset=UTF-8"
                },
                "body": {
                    "description": "Consumer Test verifies provider",
                    "name": "Consumer Test",
                    "type": "testing product"
                }
            },
            "providerState": "test demo first state"
        }
    ],
    "metadata": {
        "pact-specification": {
            "version": "2.0.0"
        },
        "pact-jvm": {
            "version": "3.3.3"
        }
    }
}

1.3。与提供者服务共享生成的PACT文件

最后,消费者应与提供者分享合同。您可以使用简单文件共享,云服务或PACT Broker。在我们的演示项目中,我们使用了文件共享,但作为一项更先进的技术,值得关注Pact broker。

第2步操作概述

2.1。启动提供程序服务
2.2。针对提供者执行请求
2.3。检查验证结果
2.1。启动提供程序服务

转到提供者服务。让我们假设我们已经收到了消费者合同,并且我们希望根据真实的提供者实施来验证它。首先,提供者服务应该启动并运行。那里有几种可能性。您可以从您的首选IDE(IntelliJ或Eclipse)启动它,或者使用已经实现的Gradle任务startProviderService

2.2。针对提供者执行请求

之后,您可以使用JUnit(请参阅ProviderJunitContractTest实现)或Pact Gradle插件:gradle pactVerify来运行验证测试。结果输出:

1
2
3
4
5
6
7
8

Verifying a pact between  product-consumer-demo and provider1
  Given test demo first state                          
  ConsumerDemoTest interaction
    returns a response which                          
      has status code 200 (OK)                        
      includes headers
        "Content-Type" with value "application/json;charset=UTF-8" (OK)
      has a matching body (OK)

2.3。检查验证结果

在这种情况下,一切似乎都按预期工作。结果应该与消费者分享,然后给消费者一个绿灯以便在生产中部署其服务。

但是,我们也想监视在发生问题时会发生什么。因此,出于演示目的,我们将向提供商服务注入失败并添加第二个消费者服务。新消费者只使用产品名称类型,而第一位消费者使用产品名称类型说明。这个想法在下面的图6中给出。

我们 通过删除它来更改提供程序属性  说明

1
2
3
4
5
6

public class Product {
    private Integer id;
    private String name;
    private String type;
    ...
}

然后我们运行我们的协议验证步骤(请注意,现在配置的消费者是两个)。结果输出:

1
2
3
4
5
6
7
8
9
10
11

Failures:
0) Verifying a pact between product-consumer-demo and provider1 - ConsumerDemoTest interaction Given test demo first state returns a response which has a matching body
      $.body -> Expected description='Consumer Test verifies provider' but was missing
      Diff:
      @1
      -    "description": "Consumer Test verifies provider",
      +    "id": 537,
          "name": "Consumer Test",

因此,我们可以看到我们违反了合同,但仅限于第一位消费者。这是因为它使用description属性,而第二个不使用它,因此它的合同通过。

概要

微服务的引入为测试带来了新的挑战。实际上,如何测试这种分布式微服务的组合,对用户来说,就像是一项服务。在敏捷测试金字塔众所周知的方法之上,我们可以做的最低限度是开始定义我们的合同。根据消费者API要求,在提供商服务实施之前,这些合同肯定可以编写。消费者驱动的合同测试创建了对支持它的框架的需求。在这篇博文中,我们介绍了PACT,下面我们将讨论  Spring Cloud Contract。

原文链接:https://blog.novatec-gmbh.de/introduction-microservices-testing-consumer-driven-contract-testing-pact/

基于PACT框架的契约测试在微服务架构中的应用相关推荐

  1. 微服务架构中10个常用的设计模式

    从软件开发早期(1960 年代)开始,应对大型软件系统中的复杂性一直是一项令人生畏的任务.多年来为了应对软件系统的复杂性,软件工程师和架构师们做了许多尝试:David Parnas 的模块化和封装 ( ...

  2. 基于Spring Boot和Spring Cloud实现微服务架构学习--转

    原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...

  3. 基于 Spring Boot 和 Spring Cloud 实现微服务架构

    前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而来,很多描述的重点也都偏向于作者自身碰到的问题,这样就很容易让你理解和操作出现偏差 ...

  4. 基于Spring Boot和Spring Cloud实现微服务架构学习

    目录 Spring 顶级框架 Spring cloud子项目 WHAT - 什么是微服务 微服务简介 微服务的具体特征 SOA vs Microservice HOW - 怎么具体实践微服务 客户端如 ...

  5. 基于Spring Boot和Spring Cloud实现微服务架构

    |来源:龙果学院 |链接:https://www.roncoo.com/article/detail/132858 前言: 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道 ...

  6. PPT分享:基于事件解决微服务架构中分布式数据问题

    简介 本文主要介绍世界级软件架构师 Chris Richardson在2021年最新分享的PPT.PPT分享的标题是<Events to the rescue: solving distribu ...

  7. 微服务架构中熔断器_基于 Golang 语言的微服务熔断器

    背景 从单体服务拆分到微服务过程中,原来模块间交互逐渐抽离成远程调用,可能http,rpc,tcp,,,等等,那么这些模块在调用中一定存在某种依赖关系. 这时一旦下游某个服务超时或者down,请求量还 ...

  8. c语言进程间通信架构,构建微服务之:微服务架构中的进程间通信

    这是使用微服务架构构建应用系列的第三篇文章.第一篇文章介绍了微服务架构模式并讨论了使用微服务的优势和劣势 :第二篇文章介绍了应用的客户端如何通过API网关作为中介实现服务间的通信:在这篇文章中我们将看 ...

  9. 微服务架构中职能团队的划分

    传统单体架构将系统分成具有不同职责的层次,对应的项目管理也倾向于将大的团队分成不同的职能团队,主要包括:用户交互UI团队.后台业务逻辑处理团队与数据存取ORM团队.DBA团队等.每个团队只对自己分层的 ...

最新文章

  1. linux open()调用的注意事项
  2. matlab 填充斜线,请教一个关于柱状图的问题--填充采用斜线之类的,不能是颜色...
  3. win10 修改软件、应用、游戏安装的默认目录
  4. Windows下tomcat进程监控批处理程序
  5. python print不换行输出_python中print如何不换行输出
  6. 史上最全的Nokia3230参数大全
  7. R语言maps包绘制世界地图并存为矢量图 超基础!
  8. (C++)设计算法求集合{1,2,...,n}的幂集
  9. 0宽字符隐藏文本加密及原理
  10. 斯坦福大学公开课:机器学习的动机与应用
  11. github建立自己的个人网站
  12. 什么是固定资产管理系统?用固定资产管理系统管理固定资产的好处?
  13. 第一篇博客-Sql排名函数DENSE_RANK
  14. 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇一:WPF常用知识以及本项目设计总结
  15. nsis安装包(五)_手把手教NIS Edit安装向导的使用
  16. 在RT-Thread STM32F407平台下配置SPI flash为U盘
  17. 生化危机6pc测试软件,《生化危机6》PC测试程序发布 赶紧检测下你的电脑吧!...
  18. worker服务器推送消息,浏览器中serviceWorker用法
  19. 我的物联网项目(二十) 合伙人羊毛党
  20. 洛谷 P4147 玉蟾宫 题解【悬线dp】

热门文章

  1. MATLAB中dec2bin函数的说明
  2. 一文读懂信息安全中的恶意代码、病毒、木马、蠕虫......
  3. 请用正则表达式匹配出QQ号(假设QQ号码为5—10位);
  4. for语句、while语句及循环结束语句的介绍
  5. 常见的业务逻辑漏洞-整合篇
  6. 【SequoiaDB巨杉数据库】Cmd-runJS
  7. 个性化教育中的自然语言理解和生成
  8. mysql out of memory_mysqld: Out of memory 解决办法(mysql)
  9. Allegro做中文丝印竟如此简单
  10. 【spring】AOP引入的使用与源码分析