在微服务架构下,你的服务可能由不同的团队提供和维护,在这种情况下,接口的开发和维护可能会带来一些问题,比如服务端调整架构或接口调整而对消费者不透明,导致接口调用失败。
为解决这些问题,Ian Robinson提出了一个以服务消费者定义契约为驱动的开发模式:“Consumer-Driver Contracts(CDC)”,就是:消费者驱动契约。
通常我们开发中主要由服务提供方约定接口,虽然提供方架构调整或改变接口之前通常会通知消费者,但可能还存在上述风险,如果上线出现问题就GG了,而CDC则是以消费者提出接口契约,交由服务提供方实现,并以测试用例对契约进行产生约束,所以服务提供方在满足测试用例的情况下可以自行更改接口或架构实现而不影响消费者。
消费者驱动的契约测试(Consumer-Driven Contracts,简称CDC),是指从消费者业务实现的角度出发,驱动出契约,再基于契约,对提供者验证的一种测试方式。
如果你目前使用SpringCloud作为微服务基础环境,那么集成SpringCloud Contracts也是比较好的选择。
原本你要测试的话必须启动相应的服务。像下面这样:
使用了Spring Cloud Contract之后,你就不需要启动这么多的服务了。像下面这样:
也许你发现了,出现了一个新的生物,叫STUB。这是个什么东西呢?稍后会详细说,这里你先就认为它可以模拟出provider,然后消费者直接调用就可以模拟服务调用了。
是不是不错。
好,接下来我们透过代码来详细的讲解下这个套件吧。
我们接下来模拟一个流程。现在有两个团队,分别负责不同的服务。
这里就假设有provider团队和consumer团队。那么当provider团队的服务还没有开发好,或者provider的团队的服务没有在启动的时候,我们可不可以进行开发呢?
答案是可以的。
契约(Contract)
这里引入一个重要的概念,就是契约,Contract。这是什么呢?很简单,就是provider和consumer事先要约定好一个接口的规范,之后双方提供服务接口和消费服务接口都要按照这个契约来。
先来看看代码的基本结构:
分别有三个模块:common、consumer、provider。
接下来一一介绍:
Common模块
然后分别有两个类,一个是分页Page实体,另外一个是Customer实体类。
1、Customer:
2、Page:
待会我们会在provider和consumer中都用到。
Provider程序
先来看看pom依赖:**
1、引入spring-cloud-starter-contract-verifier
注:引入verfier是为了验证是否符合契约
2、引入spring-cloud-contract-maven-plugin:
baseClassForTests这个就是你要符合契约的测试代码。
这里主要介绍和contract紧相关的依赖。其他依赖你可以到具体的源码中查看(点击“阅读原文”)。
3、契约
先来看看契约的定义是什么样。
我们在provider的test下的resources下新建了一个contracts目录,这个就是放置契约的地方,里边有个shouldReturnAllCustomers的groovy文件就是我们的契约,来看看吧。
shouldReturnAllCustomers.grovvy:
我们看到有三个部分:description、request、response。通过request定义了请求时的url和method,然后通过response约定返回时的headers和body信息。
契约采用groovy的DSL描述,所以一目了然,就是通过url:/ api / customers来取得一个json格式的客户列表,返回两个客户信息。
契约约定好了。接下来我们就来实现这个契约吧:
CustomerRepository:
CustomerRestController:
接下来我们就来生成stub jar文件。这个jar文件的目的就是可以被消费者拿来当做一个模拟服务来启动然后在本地跑测试用例,而不需要真正的服务提供者启动。
4、生成stub jar:
执行install把stubjar包安装到本地(在正式开发的时候可以deploy仓库)
-
clean install -Dmaven.test.skip=true
发现已经安装好了:
这时候我们可以通过contract插件简单的看下效果:
run之后,我们会看到通过启动stub 的jar文件,我们可以模拟一个真实的服务:
然后我们访问:http://localhost:8080/api/customers
可以发现,我们通过契约然后生成了stub jar,然后启动stub jar居然模拟了契约约定好的服务。
接下来我们去编写一个consumer程序吧。
另外启动stub也可以通过以下命令来启动:
-
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:
通过这个依赖,我们一会就可以启动stub来模拟启动一个契约好的服务了。
接下来,作为消费者端,来写一个测试用例,来模拟测试服务吧:
2、SpringCloudContractConsumerApplicationTests:
这里使用到了以下的注解:
-
@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在你本机上安装过就可以了。
然后运行测试:
发现测试通过了。
总结
你应该发现了,我们根本没有真正的启动服务提供者,而是在本地启动了stub就模拟测试了一次服务调用。
本文首先向你介绍了消费者驱动测试的基本背景,然后我们编写了一个服务的契约,并介绍如何定义Spring Cloud Contract的契约,然后我们借助contract maven插件生成了stub jar包,然后install到本地。接着我们编写了消费者端的测试用例,通过stub runner来模拟服务提供者完成了一次消费者调用服务的测试。
契约测试的工具除了Spring Cloud Contract外,还有其他的一些工具可供你选择,比如:Janus,Pact,Pacto等。