消费者驱动的微服务契约测试套件:Spring Cloud Contract_乔志勇笔记的博客 - CSDN 博客

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

微服务架构下,你的服务可能由不同的团队提供和维护,在这种情况下,接口的开发和维护可能会带来一些问题,比如服务端调整架构或接口调整而对消费者不透明,导致接口调用失败。

为解决这些问题,Ian Robinson 提出了一个以服务消费者定义契约为驱动的开发模式:“Consumer-Driver Contracts(CDC)”,就是:消费者驱动契约。

通常我们开发中主要由服务提供方约定接口,虽然提供方架构调整或改变接口之前通常会通知消费者,但可能还存在上述风险,如果上线出现问题就 GG 了,而 CDC 则是以消费者提出接口契约,交由服务提供方实现,并以测试用例对契约进行产生约束,所以服务提供方在满足测试用例的情况下可以自行更改接口或架构实现而不影响消费者。

消费者驱动的契约测试(Consumer-Driven Contracts,简称 CDC),是指从消费者业务实现的角度出发,驱动出契约,再基于契约,对提供者验证的一种测试方式。

如果你目前使用 SpringCloud 作为微服务基础环境,那么集成 SpringCloud Contracts 也是比较好的选择。

原本你要测试的话必须启动相应的服务。像下面这样:

https://img-blog.csdnimg.cn/img_convert/bb1051397d5215f766a5640a1653d7ce.png;wxfrom=5&wx_lazy=1

使用了 Spring Cloud Contract 之后,你就不需要启动这么多的服务了。像下面这样:

https://img-blog.csdnimg.cn/img_convert/a90463b5781fab0f8f012d120898e88d.png

也许你发现了,出现了一个新的生物,叫 STUB。这是个什么东西呢?稍后会详细说,这里你先就认为它可以模拟出 provider,然后消费者直接调用就可以模拟服务调用了。

是不是不错。

好,接下来我们透过代码来详细的讲解下这个套件吧。

我们接下来模拟一个流程。现在有两个团队,分别负责不同的服务。

这里就假设有 provider 团队和 consumer 团队。那么当 provider 团队的服务还没有开发好,或者 provider 的团队的服务没有在启动的时候,我们可不可以进行开发呢?

答案是可以的。

契约(Contract)

这里引入一个重要的概念,就是契约,Contract。这是什么呢?很简单,就是 provider 和 consumer 事先要约定好一个接口的规范,之后双方提供服务接口和消费服务接口都要按照这个契约来。

先来看看代码的基本结构:

https://img-blog.csdnimg.cn/img_convert/53ea900f43b4ad9ee592055fb6e621ce.png

分别有三个模块:common、consumer、provider。

接下来一一介绍:

Common 模块

https://img-blog.csdnimg.cn/img_convert/6b4dd33e2267c6405f4d86d9e885fcbb.png

然后分别有两个类,一个是分页 Page 实体,另外一个是 Customer 实体类。

1、Customer:

https://img-blog.csdnimg.cn/img_convert/c201d248c3e26b148921a9e4ad6f5be7.png

2、Page:

https://img-blog.csdnimg.cn/img_convert/ea85bc1f23003e56c9ab645397281473.png

待会我们会在 provider 和 consumer 中都用到。

Provider 程序

先来看看 pom 依赖:**

1、引入 spring-cloud-starter-contract-verifier

https://img-blog.csdnimg.cn/img_convert/24b7723c74eff7f1f5b6719fe8b4852d.png

注:引入 verfier 是为了验证是否符合契约

2、引入 spring-cloud-contract-maven-plugin:

https://img-blog.csdnimg.cn/img_convert/bd2b8502a3494876d99f7d9971a31006.png

baseClassForTests 这个就是你要符合契约的测试代码。

这里主要介绍和 contract 紧相关的依赖。其他依赖你可以到具体的源码中查看(点击 “阅读原文”)。

3、契约

先来看看契约的定义是什么样。

https://img-blog.csdnimg.cn/img_convert/c20616310f613398d428d3f4f740ccaf.png

我们在 provider 的 test 下的 resources 下新建了一个 contracts 目录,这个就是放置契约的地方,里边有个 shouldReturnAllCustomers 的 groovy 文件就是我们的契约,来看看吧。

shouldReturnAllCustomers.grovvy:

https://img-blog.csdnimg.cn/img_convert/fe452b02109eb76d2c3652f631527273.png

我们看到有三个部分:description、request、response。通过 request 定义了请求时的 url 和 method,然后通过 response 约定返回时的 headers 和 body 信息。

契约采用 groovy 的 DSL 描述,所以一目了然,就是通过 url:/ api / customers 来取得一个 json 格式的客户列表,返回两个客户信息。

契约约定好了。接下来我们就来实现这个契约吧:

CustomerRepository:

https://img-blog.csdnimg.cn/img_convert/c8fab04b600c764746d0ee189d28dd53.png

CustomerRestController:

https://img-blog.csdnimg.cn/img_convert/49c45e8951c9de2b3bfad97f28449d51.png

接下来我们就来生成 stub jar 文件。这个 jar 文件的目的就是可以被消费者拿来当做一个模拟服务来启动然后在本地跑测试用例,而不需要真正的服务提供者启动。

4、生成 stub jar:

执行 install 把 stubjar 包安装到本地(在正式开发的时候可以 deploy 仓库)

  1. clean install -Dmaven.test.skip=true

发现已经安装好了:

https://img-blog.csdnimg.cn/img_convert/366314acc42d841374beba08b206aff1.png

这时候我们可以通过 contract 插件简单的看下效果:

https://img-blog.csdnimg.cn/img_convert/62040766898cc04f5b9462c31ed796a9.png

run 之后,我们会看到通过启动 stub 的 jar 文件,我们可以模拟一个真实的服务:

https://img-blog.csdnimg.cn/img_convert/756c3904dc61a844a63bb208ddcf3e18.png

然后我们访问:http://localhost:8080/api/customers

https://img-blog.csdnimg.cn/img_convert/e7251ee7fd4edbc9f510ec77ea599e90.png

可以发现,我们通过契约然后生成了 stub jar,然后启动 stub jar 居然模拟了契约约定好的服务。

接下来我们去编写一个 consumer 程序吧。

另外启动 stub 也可以通过以下命令来启动:

  1. java -jar spring-cloud-contract-provider-0.0.1-SNAPSHOT-stubs.jar --stubrunner.ids=com.importsource.springcloud:spring-cloud-contract-provider:+:8080 --stubrunner.workOffline=true

Consumer 程序

1、spring-cloud-starter-contract-stub-runner

依赖 spring-cloud-starter-contract-stub-runner:

https://img-blog.csdnimg.cn/img_convert/4d515afe42267064622ec4b48ea1edbf.png

通过这个依赖,我们一会就可以启动 stub 来模拟启动一个契约好的服务了。

接下来,作为消费者端,来写一个测试用例,来模拟测试服务吧:

https://img-blog.csdnimg.cn/img_convert/cd32469406fa9edb8df35a0556012477.png

2、SpringCloudContractConsumerApplicationTests:

https://img-blog.csdnimg.cn/img_convert/eec9ee5fab3df22487017df264d9e8a3.png

这里使用到了以下的注解:

  1. @AutoConfigureStubRunner(ids = {"com.importsource.springcloud:spring-cloud-contract-provider:+:8080"}, workOffline = true)

@AutoConfigureStubRunner

通过 @AutoConfigureStubRunner 来自动配置注入一个 StubRunner。

1、ids:

然后通过 ids 去定位到刚刚通过 clean install 好的那个 stub jar 包,然后在 8080 端口去启动这个 stub jar。

ids 的格式是长这样:

groupId:artifactId:version:classifier:port

2、workOffline = true

是指使用本地 maven 库,不要使用线上的版本,所以你只要把 Consumer 在你本机上安装过就可以了。

然后运行测试:

https://img-blog.csdnimg.cn/img_convert/def414179867b0d7df107e76b54c57bd.png

发现测试通过了。

总结

你应该发现了,我们根本没有真正的启动服务提供者,而是在本地启动了 stub 就模拟测试了一次服务调用。

本文首先向你介绍了消费者驱动测试的基本背景,然后我们编写了一个服务的契约,并介绍如何定义 Spring Cloud Contract 的契约,然后我们借助 contract maven 插件生成了 stub jar 包,然后 install 到本地。接着我们编写了消费者端的测试用例,通过 stub runner 来模拟服务提供者完成了一次消费者调用服务的测试。

契约测试的工具除了 Spring Cloud Contract 外,还有其他的一些工具可供你选择,比如:Janus,Pact,Pacto 等。