一则消费者驱动的契约测试细节全览

刘华 2019-01-22 10:18:01
 

刘华(Kenneth),就职于世界500强银行。负责基金外包业务软件开发与交付。敏捷、精益、DevOps领域专家。著有《猎豹行动——硝烟中的敏捷转型之旅》一书。

消费者驱动契约测试对于API或微服务开发非常重要,API或微服务间的集成测试不容易,且成本高昂。不管是作为API的提供者还是消费者,都无法单独完成集成测试。

 

作为消费者,要测试则需要启动提供者的服务,但它往往已经是其他团队的领地,反之亦然。提供者和消费者的开发步伐也往往不一致,导致彼此间不必要的等待时间。当测试出现问题,修复的周期也长。

 

基于以上痛点,契约测试应运而生,它解耦了API提供者和消费者间的开发与测试过程。双方只需要约定对API接口的期望(假设提供者收到怎样的请求会产生怎样的响应)并通过一份“契约”把它固化下来。彼此就可以分别围绕着这份契约按照自己的开发步伐进行独立的接口测试。

 

 

笔者最近完成了一次完整的契约测试过程,在此分享一些经验和提示给大家。

 

Spring Cloud提供了Spring Cloud Contract框架来支持契约测试。其大致过程为:

 

  • API消费者与提供者约定契约;

  • Spring Cloud Contract的Maven/Gradle Plugin会自动根据契约生成JUnit的测试程序,供API提供者来测试其行为是否满足契约的预期;

  • API提供者通过Spring Cloud Contract的Maven/Gradle Plugin根据契约生成Stub,它将模拟API提供者的行为供消费者调用来测试;

  • API提供者通过Maven/Gradle deploy把Stub发布到Nexus仓库;

  • API提供者完成开发,通过第2步的测试来验证;

  • API消费者完成开发,调用Stub来测试,可通过Nexus仓库下载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来模拟提供者的行为。其代码样例如下:

 

 

如果Stub已经通过Maven/Gradle install安装到本地仓库,可以通过本地调用。更长远的方法是调用Nexus仓库的Stub。在AutoConfigureStubRunner可以指定本地调用(代码样例中被注释的那段)还是远程调用以及远程调用的下载信息。如果Nexus需要登录,则可以在src/test/resources目录下增加application.properties配置文件,并在里面指定登录用户和密码。

 

stubrunner.username= 

stubrunner.password=

 

其他提示:

 

  • 约定契约不是一次性过程。任何一方无法通过契约测试或对约定有新的想法,都应该重新拟定契约。

  • 契约测试应该关注:

    1)检查消费者构建的请求是否匹配对应的契约;

    2)检查提供者的响应是否满足消费者的需要。

  • 契约测试不应该关注提供者或消费者的业务逻辑是否正确,这些应该由各自的功能测试或单元测试来覆盖。

 

三、写在最后

 

我目前负责开发一个API提供者,消费者需要依赖我的API。通过契约测试,一旦契约约定后,消费者便不再需要等待我这边开发完了才能测试,大大舒缓了我的开发压力。我也可以在集成测试前通过契约测试进行独立测试,大大增加对交付的信心。

 

在契约测试中,API提供者与消费者间的沟通是最重要的,约定契约是首要工作。

 

整体上来说,Spring Cloud Contract框架还是比较复杂,未到“开箱即用”的程度。初接触需要花上好几个小时来熟悉其原理和过程。 

 

相关阅读:

《面对“恐龙级”老旧系统,怎样用微服务实现敏捷交付?》

活动预告