“ 消费者驱动契约测试对于API或微服务开发非常重要,它解耦了API提供者和消费者间的开发与测试过程。”
API或微服务间的集成测试不容易,且成本高昂。不管是作为API的提供者还是消费者,都无法单独完成集成测试。作为消费者,要测试则需要启动提供者的服务,但它往往已经是其他团队的领地,反之亦然。提供者和消费者的开发步伐也往往不一致,导致彼此间不必要的等待时间。当测试出现问题,修复的周期也长。
基于以上痛点,契约测试应运而生,它解耦了API提供者和消费者间的开发与测试过程。双方只需要约定对API接口的期望(假设提供者收到怎样的请求会产生怎样的响应)并通过一份“契约”把它固化下来。彼此就可以分别围绕着这份契约按照自己的开发步伐进行独立的接口测试。
Spring Cloud提供了Spring Cloud Contract框架来支持契约测试。其大致过程为:
- API消费者与提供者约定契约;
- Spring Cloud Contract的Maven/Gradle Plugin会自动根据契约生成JUnit的测试程序,供API提供者来测试其行为是否满足契约的预期;
- API提供者完成开发,通过第2步的测试来验证;
- API提供者通过Spring Cloud Contract的Maven/Gradle Plugin根据契约生成Stub,它将模拟API提供者的行为供消费者调用来测试;
- API消费者完成开发,调用Stub来测试。
契约长啥样
—
说了这么久,这份神奇的契约长啥样呢?在Spring Cloud Contract中,它可以以Groovy DSL或YAML的形式表达。下面是样例:
Groovy DSL格式:
YAML格式:
我更推崇YAML格式,因为它书写更简洁,可读性更高。
像BDD的规格文档一样,契约是可执行的,可内化为代码的一部分,嵌入到持续集成,持续保护着系统。
开发提示
—
基类
为了让Spring Cloud Contract plugin自动生成测试代码,需要指定一个基类。而基类的命名与契约所在的目录有约定关系。
比如,如果基类命名为ConverterBase,那么契约需要放在src/test/resources/contracts/converter目录下:
以下是基类的样例:
在POM中,需要在plugin的配置中指定这个基类的package:
有了以上的这些元素,Spring Cloud Contract plugin就可以生成以下的测试代码。通过mvn test命令,便可测试API提供者(由于生成的是Class,不可以通过IDE的JUnit Runner直接执行)。
验证Stub
当契约准备好后,我们可以在API服务者端通过mvn install -DskipTests来生成Stub。在这里跳过测试是因为以下原因:
- 在以前的build过程中可能生成了过时的测试代码,这可能会导致当前的测试失败;
- API提供者的实现还没有开发完成,也会导致测试失败。
当install完成后,相应的Stub会生成。我们可以通过Spring Cloud Contract Stub Runner plugin运行Stub来模拟API提供者的行为。然后用PostMan来验证它对请求的响应是否满足预期。
消费者端的测试
消费者可以调用Stub来模拟提供者的行为。其代码样例如下: