本文转载公众号:永辉云创技术

该号由我参与维护,欢迎大家关注支持!!!

分布式研发模型演进

  • 众所周知, 分布式系统是由众多微服务构成,并按照功能模块划分后, 由不同的开发小组进行维护. 研发模型如下图所示: 开发人员完成某一个微服务的功能后, 发布测试环境交付测试团队验证. 这种工作模式的弊端是, Bug在测试环境才被暴露, 而不是在编码阶段就被发现.

  • 为了解决上述的弊端, 研发团队通常会引入了单元测试, 并使用EasyMock, Mokito等框架, 来帮助开发人员在开发阶段暴露Bug. (对DB, Redis等依赖通常使用Docker来解决, 与主题无关, 这里暂时不做过多介绍. 有兴趣的可以自己研究)

  • 在日常的研发工作中, 很多团队或多或少遇到过这种情形: 微服务提供方修改了对外接口, 导致消费方无法正常请求, 造成生产事故. 管理上的人为避免, 难免导致各种疏漏, 为此我们找到了一种智能的解决方案---消费者驱动的契约测试. 大意是这样的: 服务提供方和消费方约定共同的契约, 双方围绕契约, 进行各自的单元测试工作.

Spring Cloud Contract概要

  • 永辉云创使用Spring Cloud作为微服务基础框架, 借助Spring Cloud Contract来帮助服务提供方和消费方来制定契约. 所谓契约, 就是双方约定好的接口调用参数, 及对应的输出. 整体概览如下图所示.

  • 通过上图, 相信大家对Spring Cloud Contract有了大体的了解, 下面我们用几个关键词来描述Spring Cloud Contract的特性.

    • 用于UT

    • 定义远程服务数据

    • 自动生成测试代码

  • Spring Cloud Contract在永辉云创的具体实施步骤如下图所示, 通常, 服务提供方, 也是数据定义方. 在这里, 我们使用的了数据定义方(所有服务契约在一个工程中定义), 服务提供方, 服务消费方三方模型.

Spring Cloud Contract实践

以下内容,摘自我们推进Spring Cloud Contract落地之初,编写的技术文档。 希望给读者带来更加接地气的参考, 部分内容进行了脱敏, 请读者谅解.

数据定义方

对于请求返回数据, 所有提供方统一在spring-cloud-contract(内部项目名, 非spring cloud Contract)项目里定义, 方便大家看测试数据

原则上由服务开发定义者来提供这个groovy,但是如果时间急迫,依赖方直接编写,并有服务开发者review后也可以提交~

题外话:有些工具, 例如wiremock可以帮助录制并模拟http请求. 使用场景: 前端开发依赖于服务端提供的接口, 我们通常是等服务端开发完成后,部署到测试环境,供前端调用. 现在有了wiremock, 假设我们要开发v2版本的接口, 可以先录制v1版本的请求, 然后修改胶片为v2版本http响应. 这样就可以前端就可以在v2接口开发完成前, 愉快地进行mock请求, 减少前端对服务端接口进度的依赖.*

  • http://www.cnblogs.com/tanglang/p/4791198.html

  • http://wiremock.org/docs/running-standalone/

服务提供方

引入UT相关jar包

<!-- 集成wireMock来实现mock请求响应。wireMock会自动构建一个虚拟远程服务 --><dependency>   <groupId>org.springframework.cloud</groupId>   <artifactId>spring-cloud-contract-wiremock</artifactId>   <scope>test</scope></dependency>

<!-- 提供打包预定义数据服务 --><dependency>   <groupId>org.springframework.cloud</groupId>   <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>   <scope>test</scope></dependency>

<!-- 自动生成单元测试代码 --><dependency>   <groupId>org.springframework.cloud</groupId>   <artifactId>spring-cloud-starter-contract-verifier</artifactId>   <scope>test</scope></dependency>

<!-- 依赖数据定义方 --><dependency>   <groupId>com.yonghui</groupId>   <artifactId>spring-cloud-contract</artifactId>   <version>1.0-SNAPSHOT</version></dependency>

配置UT代码生成器插件

该插件可以帮助我们生成自动化代码, 执行命令"mvn clean install -Dmaven.test.skip=false"后, 即可看到target目录自动生成的UT代码. 注意, 插件要>1.1.4.RELEASE, (该版本修复了long类型的dsl生成测试代码报错的问题)

<!-- UT代码生成器插件 --><plugin>   <groupId>org.springframework.cloud</groupId>   <artifactId>spring-cloud-contract-maven-plugin</artifactId>   <version>1.1.4.RELEASE</version>   <extensions>true</extensions>   <configuration>      <!-- packageWithBaseClasses 设置基类包目录,使用baseClassMappings替代,不使用 -->      <!--<packageWithBaseClasses>contract</packageWithBaseClasses>-->      <!--baseClassMappings 设置生成测试的基类。用包名的正则来进行匹配 -->      <contractsWorkOffline>true</contractsWorkOffline>      <baseClassMappings>         <baseClassMapping>            <contractPackageRegex>.*</contractPackageRegex>            <baseClassFQN>contract.ContractBase</baseClassFQN>         </baseClassMapping>      </baseClassMappings>      <!--basePackageForTests 设置测试的生成的位置 -->      <basePackageForTests>verifier.tests</basePackageForTests>      <contractDependency>         <groupId>com.yonghui</groupId>         <artifactId>spring-cloud-contract</artifactId>         <version>1.0-SNAPSHOT</version>         <classifier>stubs</classifier>      </contractDependency>      <!--contractsPath  设置contracts路径-->      <contractsPath>contracts/xxx-mst-center</contractsPath>   </configuration>   <dependencies>      <dependency>         <groupId>org.codehaus.groovy</groupId>         <artifactId>groovy</artifactId>         <version>2.4.12</version>      </dependency>    <dependency>        <groupId>com.yonghui</groupId>        <artifactId>spring-cloud-contract</artifactId>        <version>1.0-SNAPSHOT</version>        <classifier>stubs</classifier>    </dependency>   </dependencies></plugin>

配置UT基础类

生成UT代码时, 有需求是需要初始化数据库, 配置内置的redis, mysql. 我们使用相关的开源框架, 搭建了自己的UT基础类, 进行ut前的场景准备.

package contract.resources;

import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;import com.yonghui.junit.InmomeryDbResource;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.context.WebApplicationContext;

/** * Created by luyunfei on 09/10/2017. */public class LocationDbResource extends InmomeryDbResource {

    public LocationDbResource() {        // 初始化内置mysql, UT执行时, 会使用flyway进行初始化相关的表        super(40200, "xxx_mst_center");    }

    @Override    protected void before() throws Throwable {        super.before();        // 初始化这个UT msql的相关数据        runResourceFile(dbName, "sql/contract/mst_location.sql");    }}
package contract;

import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;import com.yonghui.junit.RedisResource;import com.yonghui.xxx.mst.center.api.impl.TestBootstrap;import contract.resources.LocationDbResource;import org.junit.Before;import org.junit.ClassRule;import org.junit.rules.ExternalResource;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.web.context.WebApplicationContext;

/** * Created by luyunfei on 27/09/2017. */@RunWith(SpringRunner.class)@SpringBootTest(classes = {TestBootstrap.class})public class xxx_mst_centerFLocationServiceBase {

    @Autowired    private WebApplicationContext context;

    // 增加这一行即可在UT中引入内置mysql, 并执行初始化    @ClassRule    public static final ExternalResource dbresource = new LocationDbResource();

    // 增加这一行即可在UT中引入内置Redis    @ClassRule    public static final ExternalResource resource = new RedisResource(20300);

    @Before    public void setUp() throws Throwable {//    RestAssuredMockMvc.standaloneSetup(new AccountController());        RestAssuredMockMvc.webAppContextSetup(context);    }}

Test文件夹下的项目启动类Bootstrap

需要注释掉consul, feign, 保证ut对外部依赖的隔离. 经过实践, 发现测试时TestBootstrap不会覆盖Bootstarp, 因此需要保持两者名字一致, 即TestBootstrap要修改文件名为Bootstrap.class

package com.yonghui.xxx.mst.center.api.impl;

import org.mybatis.spring.annotation.MapperScan;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.cloud.client.SpringCloudApplication;import org.springframework.context.annotation.ComponentScan;

/** * Created by luyunfei on 11/04/2017. */@EnableAutoConfiguration// 注意不要用SpringCloudApplication, 它会依赖consul启动, 而ut中不需要启动consul//@SpringCloudApplication@SpringBootApplication// 下面这个要注释掉, 其它和Bootstrap一样// @Import({YhConsulConfig.class,FeignConfiguration.class})// 需要引入FeignConfiguration.class, 同时增加配置spring.application.feature.enabled=false@Import({FeignConfiguration.class})@ComponentScan(basePackages = "com.yonghui.xxx")@MapperScan("com.yonghui.xxx.mst.center.mapper")public class Bootstrap {

    private static final Logger log = LoggerFactory.getLogger(TestBootstrap.class);

    public static void main(String[] args) {        SpringApplication.run(TestBootstrap.class, args);        log.info("Bootstrap started successfully");    }

}

test/resources/bootstrap.properties__增加配置(重要) __

spring.cloud.consul.enabled=falsespring.application.feature.enabled=false

服务消费方

配置和服务提供方一致, 需要调用提供方接口的测试类, 增加以下注释, 端口号不要写错了

@AutoConfigureStubRunner(ids = {"com.yonghui:xxx-mst-center-server:1.0-SNAPSHOT:stubs:5656"} ,workOffline = true)
package contract;

import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;import com.yonghui.junit.InmomeryDbResource;import com.yonghui.junit.RedisResource;import com.yonghui.xxx.inventory.center.api.impl.TestBootstrap;import org.junit.Before;import org.junit.ClassRule;import org.junit.rules.ExternalResource;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.web.context.WebApplicationContext;

/** * Created by luyunfei on 28/09/2017. */

@RunWith(SpringRunner.class)@SpringBootTest(classes = {TestBootstrap.class})@AutoConfigureStubRunner(ids = {"com.yonghui:xxx-mst-center-server:1.0-SNAPSHOT:stubs:5656"}        ,workOffline = true)public class Xxx_inventory_centerFInventoryServiceBase extends InmomeryDbResource {

    @Autowired    private WebApplicationContext context;

    @ClassRule    public static final ExternalResource resource = new RedisResource(20300);

    public xxx_inventory_centerFInventoryServiceBase() {        super(40200, "xxx_inventory_center");    }

    @Before    public void setup() throws Throwable {//    RestAssuredMockMvc.standaloneSetup(new AccountController());        RestAssuredMockMvc.webAppContextSetup(context);        super.before();        // 初始化sql        //runResourceFile(dbName, "sql/DockServiceImplTest01/dockServiceGetListTest01.sql");    }

}

热文推荐

全球最大同性交友网站 GitHub 10 岁了!

JDK 1.5 - 1.8 各版本的新特性总结

Spring Boot快速开发利器:Spring Boot CLI

IntelliJ IDEA 2018.1正式发布!还能这么玩?

消息中间件选型分析

自建API网关「架构设计篇」

其他推荐

Spring Cloud构建微服务架构:分布式配置中心(加密解密)

Spring Boot使用@Async实现异步调用:线程池的优雅关闭

Spring Boot使用@Async实现异步调用:自定义线程池

Spring Boot 2.0正式发布,升还是不升呢?

Spring Boot 2.0 新特性概览

Spring Boot/Cloud干货汇总

长按指纹

一键关注

深入交流、更多福利

扫码加入我的知识星球


点击 “阅读原文” 看看本号其他精彩内容

Spring Cloud Contract 契约测试实践相关推荐

  1. 消费者驱动的微服务契约测试套件:Spring Cloud Contract

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

  2. 消费者驱动的微服务契约测试套件Spring Cloud Contract

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

  3. 消费者驱动的契约测试 Spring Cloud Contract介绍

    消费者驱动的契约测试 Spring Cloud Contract介绍 什么是契约测试 测试是软件流程中非常重要,不可或缺的一个环节.一般的测试分为单元测试,集成测试,端到端的手工测试,这也是构成测试金 ...

  4. Spring Cloud Contract实践

    1.Spring Cloud Contract简介 Spring Cloud Contract是一个总体项目,其中包含帮助用户成功实施消费者驱动合同方法的解决方案.目前,Spring Cloud Co ...

  5. 契约测试之Spring Cloud Contract

    在微服务架构下,服务间会通过某种形式的消息传递或API调用进行耦合,这让服务的集成以及测试变成了非常具有挑战的一件事.早在微服务流行之前,就有人提出了消费者驱动契约(Consumer-driven c ...

  6. Spring系列学习之Spring Cloud Contract测试消息传递

    英文原文:https://spring.io/projects/spring-cloud-contract 目录 概述 特性 Spring Boot配置 Server / Producer方面 Cli ...

  7. spring cloud contract的应用实现与概念理解-服务请求者一侧的落地-细节较多避免踩坑卡壳

    笔者的经验认为,微服务的出现,是为了应对传统SOA架构在多服务背景下的疲软,本质上是SOA的进一步衍生.是一种治理服务的手段.而微服务之所以能够解决传统SOA.单块大单体程序的问题,原因在于微服务自身 ...

  8. spring cloud contract的应用实现与概念理解-服务提供者一侧的落地

    如题,本文是在前一篇"spring cloud contract的应用实现与概念理解-服务请求者一侧的落地"的基础上,续写服务提供者一侧的有关实现与理解. 通过对官网文章的学习和编 ...

  9. Marcin Grzejszczak访谈:Spring Cloud Contract

    Marcin Grzejszczak是Pivotal的一名软件工程师.目前,他在从事Spring Cloud Contract的开发,这是一个消费者驱动的.面向Java的契约框架.为了了解该框架的一些 ...

最新文章

  1. RabbitMQ 延迟队列实现定时任务的正确姿势,你学会了么?
  2. Git学习系列(二)创建本地仓库及文件操作
  3. 无法上外网, ping网关ping不通的解决——arp命令
  4. unittest单元测试框架总结
  5. 抑制恐慌,互联网能够做些什么?
  6. python学习笔记--turtle库的使用
  7. [Java基础]Collections概述和使用
  8. C语言2020年作业,2020年c语言上机报告范文【四篇】
  9. 中国最闷声发大财的城市,人均GDP超杭州
  10. 宁德时代在川签署四方协议 加快全省锂矿资源勘查开发
  11. janusgraph整合mysql_图解图库JanusGraph系列-一文知晓“图数据“底层存储结构
  12. java 读取 tgz_java – 从Spark中的压缩中读取整个文本文件
  13. 在Angular中添加第三方库jQuery、bootstrap
  14. 2 抽象工厂模式(Abstract Factory)
  15. 病毒是什么?地球上万物都是相生相克的,那么病毒在自然界的天敌是什么?...
  16. 手风琴控件android,ExpandableListView实现手风琴效果
  17. C#窗体程序随电脑分辨率自动调整大小
  18. 科普类:什么是量子霸权?
  19. 概率笔记5——概率分布
  20. 不同操作系统中的换行符,回车符 \r , \n

热门文章

  1. linux c 宏 LONG_MAX LLONG_MAX
  2. linux c 命令行解析函数 getopt getopt_long
  3. golang TCP Socket编程
  4. Linux里如何查找文件内容 grep
  5. 使用 diff 查找文件的差异并生成补丁文件修补
  6. 进程,线程与信息共享
  7. Python3猜数字小游戏
  8. desc 枚举类型id_枚举系列(四):实现接口的枚举类
  9. oracle日志版本不同,Oracle重做日志文件版本不一致问题处理
  10. 数据结构 图的深度优先遍历 C