Spring Cloud中的契约测试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaoruda/article/details/88764630


这里契约测试基于Spring Cloud Contract来编写,大致流程如下所示:

  • 在Provider使用groovy DSL编写Contract
  • 通过Contract Verifier验证Contract所生成的测试
  • 测试通过后将Artifact(stub.jar)发布到Maven仓库中
  • 在Consumer端pull下相应的Artifact(stub.jar)
  • 运行测试,同时以Artifact作为基础设施启动Stub server
  • 在测试中向Stub server发送请求验证API的正确性

Provider

添加gradle插件和依赖

buildscript {
    ext {
        springBootVersion = '2.0.5.RELEASE'
        springColudContractVersion = '2.0.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        // ......
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.springframework.cloud:spring-cloud-contract-gradle-plugin:${springColudContractVersion}")
        // ......
    }
}

// ......
apply plugin: 'groovy'
apply plugin: 'spring-cloud-contract'
// ......


dependencies {
    // ......
    testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
    // ......
}

编写Contract

按照Spring Cloud Contract的约定,groovy编写的DSL文件需要在在src/test/resources/contracts/目录下。

import org.springframework.cloud.contract.spec.Contract

Contract.make {
  request {
    url "/goods"
    method GET()
  }

  response {
    status 200
    headers {
      contentType applicationJson()
    }
    body '''
            [{
                "id"   : 1,
                "name" : "123",
            },
            {
                "id"   : 2,
                "name" : "456",
            },
            {
                "id"   : 3,
                "name" : "789",
            }]
        '''
  }
}

创建测试基类

给从Contract生成的测试类指定一个基类。按照Spring Cloud Contract的约定基类以Base结尾。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = GoodsServiceApplication.class)
public class ContractVerifierBase {
  @Autowired
  private WebApplicationContext applicationContext;

  @Before
  public void setup() {
    RestAssuredMockMvc.webAppContextSetup(applicationContext);
    //RestAssuredMockMvc.standaloneSetup(goodsController); 也可以使用standaloneSetup启动单个controller。
  }
}

在gradle指定gradle plugin使用的基类

contracts {
    packageWithBaseClasses = 'contracts'
}

这样会到test/java/contracts目录中找以Base结尾到基类。按照Spring Cloud Contract的,如果groovy dsl文件在目录src/test/resources/contract/foo/bar/baz/中,而且packageWithBaseClasses
设置为com.example.base,则会到com.example.base目录中寻找名为BarBazBase的基类。也就是说会以最后两级目录名作为基类名字。

运行测试

 ./gradlew generateContractTests // 根据Contract在build/generated-test-sources/contracts/目录下生成测试类
 ./gradlew test //运行测试

发布Artifact

在测试通过之后需要将stub jar包发布到consumer可以获取到的地方,在这里发布到本地的maven仓库。

publishing {
    publications {
        stubs(MavenPublication) {
            groupId 'com.learning'
            artifactId "goods-service"
            version '0.0.1'
            artifact verifierStubsJar
        }
    }
    repositories {
        mavenLocal()
    }
}

Consumer

在Provider对契约进行验证并将stub.jar发布在maven仓库中后,需要在Consumer对契约进行验证。

添加gradle插件和依赖

    ext {
        springBootVersion = '2.0.5.RELEASE'
        springColudContractVersion = '2.0.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        // ......
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.springframework.cloud:spring-cloud-contract-gradle-plugin:${springColudContractVersion}")
        // ......
    }
}
// ......
dependencies {
    // ......
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
    // ......
}

配置stub runner

在application-contract-test.yml文件中添加stub runner配置

stubrunner:
  stubsMode: local
  ids:
  - com.learning:goods-service:0.0.1:stubs

stubsMode: 表示stub是在本地获取还是remote获取。这里指定为local,在本地maven仓库找stub。
ids: 指定所要获取对stub。
当然还有其他配置,有兴趣对可以自己去研究。

添加测试代码

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApiGatewayApplication.class)
@AutoConfigureStubRunner
@ActiveProfiles("contract-test")
public class GoodsClientTest {
  @Autowired
  private StubFinder stubFinder;

  @Test
  public void should_return_correct_goods() {
    int port = stubFinder.findStubUrl("com.learning", "goods-service").getPort();
    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://localhost:" + port + "/goods", List.class);
    List responseBody = responseEntity.getBody();
    assertEquals(3,responseBody.size());
  }
}

在这里使用StubFinder进行测试。当请求/goods时,会将请求转发到stub中并返回指定到契约。这样可以保证provider是按照契约返回数据到consumer中。
如果不使用StubFinder,还可以基于FeignClient去请求。

Provider相关代码
Consumer相关代码

猜你喜欢

转载自blog.csdn.net/zhaoruda/article/details/88764630