• Spring-Cloud-Contract实战


    Spring-Cloud-Contract

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

    主要应用场景

    1. 多服务、多团队系统、前后端联调使用

    若想测试应用v1,我们可以:

    • 部署所有微服务并执行端到端测试。

    优点:
    模拟生产。
    测试服务之间的真实通信。

    缺点:
    要测试一个微服务,我们必须部署6个微服务,几个数据库等。

    运行时间很长,稳定性差,容易失败。

    非常难以调试,依赖服务不受控制。

    • 在单元/集成测试中模拟其他微服务。

    优点:
    非常快速的反馈,简单易用。

    没有基础设施要求,如DB,网络等。

    缺点:

    模拟不够真实。
    部分场景测试不到。

    2. 契约工作流程

    使用契约后,测试v1就不用启动其他服务了(有依赖其他服务的数据的话)。

    通俗来讲,契约使用步骤可大致分为:

    2.1 生产者提供定义好的契约(接口,包含request Method、header,parameter,body)

    2.2 生产者生成stub jar,提供给消费者,可mvn install到Maven库(本地/远程仓库)

    2.3 消费者调用生产者的接口(从契约获取数据)


    更为详细的契约工作流程如下:

    Spring-Cloud-Contract的开发者MARCIN GRZEJSZCZAK在博客中提到:Spring Cloud Contract in a polyglot world

    The producer:(生产者)

    • Applies a Maven or Gradle Spring Cloud Contract plugin.
    • (译) 使用Maven/Gradle的Spring Cloud Contract插件。
    • Defines YAML contracts under src/test/resources/contracts/.
    • (译) 在目录src/test/resources/contracts下,定义Groovy/YAML形式的合同。
    • Generates tests and stubs from the contract.
    • (译) 从契约中生成测试类和存根
    • Creates a base class that extends the generated tests and sets up the test context.
    • (译) 创建一个基类并设置测试的上下文,用于被生成的测试类继承
    • Once the tests pass, creates a JAR with stubs classifier where contracts and stubs are stored.
    • (译) 测试通过后,会创建一个stubs-jar,其中存储了契约和存根
    • Uploads the JAR with a stubs classifier to binary storage.
    • (译) 上传stubs-jar到Maven库

    The consumer:(消费者)

    • Uses Stub Runner to fetch the stubs of the producer. Stub Runner starts in memory HTTP servers (by default, those are WireMock servers) fed with the stubs.
    • (译) 使用stub Runner获取生产者的存根。Stub Runner在内存中启动了HTTP服务器(默认情况下,那些是WireMock服务器)。
    • Runs tests against the stubs.
    • (译) 针对存根运行测试。

    Consequently, using Spring Cloud Contract and Contract Testing gives you:
    (译) 因此,使用契约和契约测试会带给你

    • stubs reliability: They were generated only after the tests have passed.
    • (译) 存根可靠性:测试通过才生成
    • stubs reusability: They can be downloaded and reused by multiple consumers.
    • (译) 存根可重用性:可被多个消费者下载,使用

    3. 使用契约-Producer side(服务提供端)

    3.1 添加依赖&插件

    <!-- 自动生成单元测试代码 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-contract-verifier</artifactId>
                <version>2.0.3.BUILD-SNAPSHOT</version>
                <scope>test</scope>
            </dependency>
            
            <!--该插件自动生成测试类、stubs(存根)-->
            
            <plugin>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
                    <version>2.0.2.BUILD-SNAPSHOT</version>
                    <extensions>true</extensions>
                    <configuration>
                        <!--测试基类-->
                        <baseClassForTests>com.xiaobai.producer.BaseMock</baseClassForTests>
                    </configuration>
                </plugin>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.2 创建测试基类

    创建插件指定的测试基类

    BaseMock.java

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    //测试的application context会被关闭,同时缓存会清除
    @DirtiesContext
    @AutoConfigureMessageVerifier
    public class BaseMock {
    
        @Autowired
        private WebApplicationContext context;
    
        @Before
        public void setUp() {
    
            //使用上下文构建
            RestAssuredMockMvc.webAppContextSetup(context);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.3 Producer side-待测试的接口

    @RestController
    @RequestMapping("/user")
    @Slf4j
    public class UserController {
       
        @Autowired
        private UserMapper userMapper;
        
        @GetMapping("/{id}")
        public UserPO getOneUser(@PathVariable("id") Integer id){
        /**
        (接口开发完成的情况)接口的返回结果跟契约定义的结果需一致
        ,否则生成的测试类,断言会不通过,也就无法生成stub jar了。这种情况应该是契约的正确使用方式,消费者定义契约,验证生产者的接口是否符合契约,符合才能构建成功。
        (接口业务代码未实现的情况)可以不需要接口,只写契约,Maven install时,只需mvn clean install -DskipTests,即可跳过verifier的测试,这种情况我是用来将生成的.json扔到mock服务器上。
        **/
            return userMapper.selectByPrimaryKey(id);
        }
        
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.4 Producer side-添加合同

    合同默认位置src/test/resources/contracts,
    支持Groovy或yaml编写的合同定义语言(DSL)

    shouldReturnOneUserSuccess.groovy:

    import org.springframework.cloud.contract.spec.Contract
    
    Contract.make {
        //ignored()
        description "should return one user success"
        request {
            method 'GET'
            urlPath('/user/1')
        }
        response {
            status 200
            body(
                    '''
                    {
        "id": 1,
        "childName": "小白",
        "parentName": "大白",
        "phone": "18880000"
    }
                    '''
            )
            headers {
                header('Content-Type', 'application/json;charset=UTF-8')
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.5 Maven install(Producer side生成stubs-jar)

    (接口未写业务代码的情况) mvn clean install -DskipTests

    (接口已实现业务代码的情况) 直接install,插件会自动生成测试类(1),合同(2),映射的json(3),存根jar(4)

    生成的测试类ContractVerifierTest.java

    合同(2)即是我们3.4步骤书写的契约文件

    映射的json文件(3),该文件可放到Mock服务器执行,后面会提到。

    存根jar(4)已安装到本地Maven仓库,可提供给其他服务调用,后面会调用到。

    4.Consumer Side(消费存根)

    消费存根jar可以用Fegin,RestTemplate…这里笔者使用RestTemplate.

    这里提一下,Fegin消费存根需要在application.yml增加stubrunner属性,如下:

    #Fegin消费存根需增加的配置,意思是哪个jar包对应哪个Fegin,这样就无需注册中心了。
    stubrunner:
    	ids-to-service-ids:
    		需要消费的jar包artifactID1:Fegin对应的value属性值1
    		需要消费的jar包artifactID2:Fegin对应的value属性值2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.1 Consumer Side-添加依赖

    <!-- 自动下载、运行stub-jar -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-contract-stub-runner</artifactId>
                <version>2.0.2.RELEASE</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.2 写测试类(消费存根)

    ConsumerTest.java

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = ConsumerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    //初始化测试配置,测试controller需要
    @AutoConfigureMockMvc
    @AutoConfigureJsonTesters
    @AutoConfigureStubRunner(ids = {"com.xiaobai:producer:+:stubs:10001"},stubsMode = StubRunnerProperties.StubsMode.LOCAL)
    @Slf4j
    public class ConsumerTest {
        @Autowired
        private RestTemplate restTemplate;
    
        @Test
        public void testMethod() throws Exception {
            ResponseEntity<JSONObject> response = restTemplate.exchange(
                    "http://localhost:10001/user/1",
                    HttpMethod.GET,
                    null,
                    JSONObject.class
            );
            log.info("测试数据:"+ response.getBody().toJSONString());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    @AutoConfigureStubRunner自动下载存根,
    ids属性格式
    groupId:artifactId:version:classifier:port

    运行该测试类

    当测试上下文启动,无需启动生产者的服务,在测试类调用生产者的接口不会404,因为Spring Cloud Contract Stub Runner将自动启动测试中的WireMock服务器并使用从服务器端生成的存根来提供返回数据。

    5.主要应用场景

    消费者驱动契约,生产者根据契约进行开发,spring-cloud-contract-verifier确保了生产者的接口一定符合契约,才能构建成功。契约需要双方共同遵守,任意一方修改了契约,可能会导致测试失败。 如消费者更新了契约(旧版->新版),而生产者未更新业务代码,那么生产者的构建就会导致失败。
    So,基于契约开发才是spring-cloud-contract的正确打开方式。

    5.1 多服务、多团队,跨服务测试

    当涉及多个系统时,可以通过公共的库,下载stubs-jar即可进行本地跨服务测试。

    5.2 使用契约进行联调

    如接口提供方,调用方存在多个团队,初期可使用契约进行联调。
    如,前端需要后端提供接口,当后端开发滞后时,可先定义契约的入参、回参,部署到Mock服务器,前端调用Mock的请求,返回定义好的数据,这样就不会阻塞前端工作。

    WireMock服务器可以在自己的进程中运行,并通过Java API,JSON over HTTP或JSON文件进行配置。

    WireMock没怎么研究,我只会一种方式:将生产者生成的.json文件(/target/stubs/MERA-INF/groupId/artifactID/version/mappings下),扔到WireMock的/mapping目录下

    5.2.1 下载WireMock standalone(不推荐这种方式)

    (推荐) tips:将所有的.json文件扔到mock服务器未免太麻烦,stub runner将.json打包成stubs-jar,然后直接运行该jar也可达到相同效果
    参考官方文档https://cloud.spring.io/spring-cloud-contract/single/spring-cloud-contract.html#_stub_runner_server_fat_jar

    以下方式略显麻烦,可参考上面的stub_runner_server_fat_jar

    http://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/2.20.0/wiremock-standalone-2.20.0.jar

    运行在本地或者服务器上

    java -jar wiremock-standalone-2.18.0.jar --port 10001


    启动Mock服务器会在当前目录生成2个空文件夹:__files和mappings。__files是放上传/下载/录制文件用的,mappings存放.json.

    Tips:添加修改mapping文件后,都需要重启服务才能生效

    5.2.2 .json放到__mapping下(不推荐)


    将该.json放到Mock服务器的_mapping下。重启Mock的jar。

    5.2.3 访问mock接口


    此功能好像很多工具都可以实现,如淘宝的rap接口管理工具…

    5.3 确保接口符合契约

    如果使用spring-cloud-contract-verifier生成测试类的话,接口的业务代码返回的数据一定符合契约时,才能构建成功。这里也可用于单元测试,验证自己的接口格式,好处是不用写单元测试代码(自动生成)。

    6.自动化

    我认为, 契约正确打开方式应该是:

      1. 消费者定义契约(.groovy 或.yaml),发布到公共契约库。
      2. 生产者拉取契约,放至/src/test/resources/contracts下,然后生产者实现接口业务(如业务未实现的情况下,可以直接写.json到WireMock服务器下,这样也可用于给消费者通过Rest调用),spring-cloud-contract-verifier会验证接口是否符合契约的预期返回值,符合则构建stubs-jar成功。
      3. 消费者通过Stubs Runner远程下载,运行生产者的stubs-jar,即可进行契约测试,消费对方的契约。
        未实现…-.-

        上图参考的链接
        https://piotrminkowski.wordpress.com/tag/spring-cloud-contract/
        可供参考:
        https://www.baeldung.com/spring-cloud-contract
  • 相关阅读:
    第七节:Asp.Net Core内置日志、将EF生成的SQL输出到控制台
    自学Zabbix3.6.2-触发器triggers severity严重程度
    自学Zabbix3.6.1-触发器triggers创建
    自学Zabbix3.5.7-监控项item-Applications
    自学Zabbix3.5.6-监控项item-Value mapping值映射
    自学Zabbix3.5.5-监控项item-User parameters(自定义key)
    自学Zabbix3.5.4-监控项item-History and trends
    自学Zabbix3.5.3-监控项item-zabbix agent 类型所有key
    自学Zabbix3.5.2-监控项item-types监控类型
    自学Zabbix3.5.1-监控项item-key介绍
  • 原文地址:https://www.cnblogs.com/zgq123456/p/13994601.html
Copyright © 2020-2023  润新知