91. Spring Cloud Contract Verifier Setup
您可以通过以下方式设置 Spring Cloud Contract Verifier:
91.1 Gradle Project
要了解如何为 Spring Cloud Contract Verifier 设置 Gradle 项目,请阅读以下部分:
91.1.1 先决条件
在 order 中使用 Spring Cloud Contract Verifier 和 WireMock,你可以使用 Gradle 或 Maven 插件。
如果要在项目中使用 Spock,则必须单独添加
spock-core
和spock-spring
模块。检查Spock docs 了解更多信息
91.1.2 添加带有依赖项的 Gradle 插件
要添加带依赖项的 Gradle 插件,请使用与此类似的 code:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}"
}
}
apply plugin: 'groovy'
apply plugin: 'spring-cloud-contract'
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifier_version}"
}
}
dependencies {
testCompile 'org.codehaus.groovy:groovy-all:2.4.6'
// example with adding Spock core and Spock Spring
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'
testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
91.1.3 Gradle 和 Rest Assured 2.0
默认情况下,Rest Assured 3.x 将添加到 classpath。但是,要使用 Rest Assured 2.x,您可以将其添加到插件 classpath 中,如下所示:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}"
classpath "com.jayway.restassured:rest-assured:2.5.0"
classpath "com.jayway.restassured:spring-mock-mvc:2.5.0"
}
}
depenendencies {
// all dependencies
// you can exclude rest-assured from spring-cloud-contract-verifier
testCompile "com.jayway.restassured:rest-assured:2.5.0"
testCompile "com.jayway.restassured:spring-mock-mvc:2.5.0"
}
这样,插件会自动看到 Rest Assured 2.x 出现在 classpath 上,并相应地修改导入。
91.1.4 Gradle 的快照版本
将附加快照 repository 添加到 build.gradle 以使用快照版本,这些版本会在每次成功 build 后自动上载,如下所示:
buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/release" }
}
}
91.1.5 添加存根
默认情况下,Spring Cloud Contract Verifier 正在查找src/test/resources/contracts
目录中的存根。
包含存根定义的目录被视为 class name,每个存根定义被视为单个测试。 Spring Cloud Contract Verifier 假定它至少包含一个 level 目录,用作 test class name。如果存在多个嵌套目录的 level,则除最后一个之外的所有目录都将用作包 name。对于 example,具有以下结构:
src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy
Spring Cloud Contract Verifier 使用两种方法创建名为defaultBasePackage.MyService
的测试 class:
-
shouldCreateUser()
-
shouldReturnUser()
91.1.6 运行插件
插件注册自己在check
任务之前调用。如果您希望它成为 build process 的一部分,则无需执行任何操作。如果您只想生成测试,请调用generateContractTests
任务。
91.1.7 默认设置
默认的 Gradle 插件设置会创建 build 的以下 Gradle 部分(伪代码):
contracts {
targetFramework = 'JUNIT'
testMode = 'MockMvc'
generatedTestSourcesDir = project.file("${project.buildDir}/generated-test-sources/contracts")
contractsDslDir = "${project.rootDir}/src/test/resources/contracts"
basePackageForTests = 'org.springframework.cloud.verifier.tests'
stubsOutputDir = project.file("${project.buildDir}/stubs")
// the following properties are used when you want to provide where the JAR with contract lays
contractDependency {
stringNotation = ''
}
contractsPath = ''
contractsWorkOffline = false
contractRepository {
cacheDownloadedContracts(true)
}
}
tasks.create(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateClientStubs') {
baseName = project.name
classifier = contracts.stubsSuffix
from contractVerifier.stubsOutputDir
}
project.artifacts {
archives task
}
tasks.create(type: Copy, name: 'copyContracts') {
from contracts.contractsDslDir
into contracts.stubsOutputDir
}
verifierStubsJar.dependsOn 'copyContracts'
publishing {
publications {
stubs(MavenPublication) {
artifactId project.name
artifact verifierStubsJar
}
}
}
91.1.8 配置插件
要更改默认的 configuration,请在 Gradle 配置中添加contracts
片段,如下所示:
contracts {
testMode = 'MockMvc'
baseClassForTests = 'org.mycompany.tests'
generatedTestSourcesDir = project.file('src/generatedContract')
}
91.1.9 Configuration 选项
-
testMode:定义验收测试的模式。默认情况下,模式是 MockMvc,它基于 Spring 的 MockMvc。对于真正的 HTTP calls,它也可以更改为JaxRsClient或Explicit。
-
imports:使用应包含在生成的测试中的导入创建一个 array(对于 example [56])。默认情况下,它会创建一个空的 array。
-
staticImports:使用应包含在生成的 tests(for example [57]中的静态导入创建一个 array。默认情况下,它会创建一个空的 array。
-
basePackageForTests:指定所有生成的测试的基础包。如果未设置,则从
baseClassForTests's package and from
packageWithBaseClasses. If neither of these values are set, then the value is set to
org.springframework.cloud.contract.verifier.tests`中选择 value。 -
baseClassForTests:为所有生成的测试创建 base class。默认情况下,如果使用 Spock classes,则 class 为
spock.lang.Specification
。 -
packageWithBaseClasses:定义一个包含所有 base classes 的包。此设置优先于baseClassForTests。
-
baseClassMappings:显式 maps contract 包到 base class 的 FQN。此设置优先于packageWithBaseClasses和baseClassForTests。
-
ruleClassForTests:指定应添加到生成的测试 classes 的规则。
-
ignoredFiles:使用
Antmatcher
允许定义应跳过处理的 stub files。默认情况下,它是一个空的 array。 -
contractsDslDir:指定包含使用 GroovyDSL 编写的 contracts 的目录。默认情况下,其 value 为
$rootDir/src/test/resources/contracts
。 -
generatedTestSourcesDir:指定应放置从 Groovy DSL 生成的测试的测试源目录。默认情况下,value 为
$buildDir/generated-test-sources/contractVerifier
。 -
stubsOutputDir:指定应放置从 Groovy DSL 生成的 WireMock 存根的目录。
-
targetFramework:指定要使用的目标测试 framework。目前,支持 Spock 和 JUnit,JUnit 是默认的 framework。
-
contractsProperties:包含要传递给 Spring Cloud Contract 组件的 properties 的 map。那些 properties 可能被 e.g 使用。内置或自定义 Stub 下载器。
当您要指定包含 contracts 的 JAR 的位置时,将使用以下 properties:* contractDependency:指定提供groupid:artifactid:version:classifier
坐标的依赖项。您可以使用contractDependency
闭包来设置它。 * contractsPath:指定 jar 的路径。如果下载了 contract 依赖项,则路径默认为groupid/artifactid
,其中groupid
是斜杠分隔。否则,它会扫描提供的目录下的 contracts。 * contractsMode:指定下载 contracts 的模式(JAR 是否可脱机使用,远程 etc.) * contractsSnapshotCheckSkip:如果设置为true
将不断言下载的存根/ contract JAR 是否从 remote 位置下载或本地 one(only 适用于 Maven 回购,而不是 Git 或 Pact)。 * deleteStubsAfterTest:如果设置为false
将不会从临时目录中删除任何下载的 contracts
91.1.10 单个 Base Class for All Tests
在默认的 MockMvc 中使用 Spring Cloud Contract Verifier 时,您需要为所有生成的验收测试创建基本规范。在此 class 中,您需要指向应验证的端点。
abstract class BaseMockMvcSpec extends Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(new PairIdController())
}
void isProperCorrelationId(Integer correlationId) {
assert correlationId == 123456
}
void isEmpty(String value) {
assert value == null
}
}
如果使用Explicit
模式,则可以使用 base class 初始化整个测试的应用程序,就像在常规 integration 测试中看到的那样。如果使用JAXRSCLIENT
模式,则此 base class 也应包含protected WebTarget webTarget
字段。现在,测试 JAX-RS API 的唯一选择是启动 web 服务器。
91.1.11 Contracts 的 Base Classes
如果你的 base classes 在 contracts 之间有所不同,你可以告诉 Spring Cloud Contract 插件哪个 class 应该由自动生成的测试扩展。您有两种选择:
-
通过提供
packageWithBaseClasses
遵循惯例 -
通过
baseClassMappings
提供显式映射
按照惯例
约定是这样的,如果你有一个 contract(for example)src/test/resources/contract/foo/bar/baz/
并将packageWithBaseClasses
property 的 value 设置为com.example.base
,那么 Spring Cloud Contract Verifier 假定com.example.base
包下有一个BarBazBase
class。换句话说,系统采用包的最后两部分(如果存在),并形成带有Base
后缀的 class。此规则优先于baseClassForTests。这是闭包中它如何工作的一个例子:
packageWithBaseClasses = 'com.example.base'
通过映射
您可以手动将 contract 包的正则表达式 map 映射到匹配的 contract 的 base class 的完全限定 name。您必须提供一个名为baseClassMappings
的列表,其中包含baseClassMapping
objects,它采用contractPackageRegex
到baseClassFQN
映射。考虑以下 example:
baseClassForTests = "com.example.FooBase"
baseClassMappings {
baseClassMapping('.*/com/.*', 'com.example.ComBase')
baseClassMapping('.*/bar/.*':'com.example.BarBase')
}
我们假设你有 contracts - src/test/resources/contract/com/
- src/test/resources/contract/foo/
通过提供baseClassForTests
,我们有一个后备,因为映射没有成功。 (你也可以将packageWithBaseClasses
提供为 fallback.)那样,从src/test/resources/contract/com/
contracts 生成的测试扩展com.example.ComBase
,而测试的 rest 扩展com.example.FooBase
。
91.1.12 调用生成的测试
要确保提供者端符合已定义的 contracts,您需要调用:
./gradlew generateContractTests test
91.1.13 将存根推送到 SCM
如果您正在使用 SCM repository 来保留 contracts 和 stubs,那么您可能希望自动执行将存根推送到 repository 的步骤。要做到这一点,就足以调用pushStubsToScm
任务。 例:
$ ./gradlew pushStubsToScm
在第 97.6 节,“使用 SCM Stub Downloader”下,您可以找到所有可以通过contractsProperties
字段 e.g 传递的 configuration 选项。 contracts { contractsProperties = [foo:"bar"] }
,通过contractsProperties
方法 e.g. contracts { contractsProperties([foo:"bar"]) }
,系统 property 或环境变量。
Consumer 侧的 91.1.14 Spring Cloud Contract Verifier
在 consuming 服务中,您需要以与提供者完全相同的方式配置 Spring Cloud Contract Verifier 插件。如果您不想使用 Stub Runner,则需要复制存储在src/test/resources/contracts
中的 contracts 并使用以下命令生成 WireMock JSON 存根:
./gradlew generateClientStubs
必须设置
stubsOutputDir
选项才能使存根生成生效。
如果存在,JSON 存根可以用于 consuming 服务的自动化测试。
@ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)
class LoanApplicationServiceSpec extends Specification {
@ClassRule
@Shared
WireMockClassRule wireMockRule == new WireMockClassRule()
@Autowired
LoanApplicationService sut
def 'should successfully apply for loan'() {
given:
LoanApplication application =
new LoanApplication(client: new Client(clientPesel: '12345678901'), amount: 123.123)
when:
LoanApplicationResult loanApplication == sut.loanApplication(application)
then:
loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED
loanApplication.rejectionReason == null
}
}
LoanApplication
调用FraudDetection
服务。此请求由配置有 Spring Cloud Contract Verifier 生成的存根的 WireMock 服务器处理。
91.2 Maven Project
要了解如何为 Spring Cloud Contract Verifier 设置 Maven 项目,请阅读以下部分:
91.2.1 添加 maven 插件
以类似于此的方式添加 Spring Cloud Contract BOM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-release.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
接下来,添加Spring Cloud Contract Verifier
Maven 插件:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<packageWithBaseClasses>com.example.fraud</packageWithBaseClasses>
</configuration>
</plugin>
您可以在Spring Cloud Contract Maven 插件文档(示例 2.0.0.RELEASE version)中阅读更多内容。
91.2.2 Maven 和 Rest Assured 2.0
默认情况下,Rest Assured 3.x 将添加到 classpath。但是,您可以通过将 Rest Assured 2.x 添加到插件 classpath 来使用它,如下所示:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<packageWithBaseClasses>com.example</packageWithBaseClasses>
</configuration>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-verifier</artifactId>
<version>${spring-cloud-contract.version}</version>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<version>2.5.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>2.5.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</plugin>
<dependencies>
<!-- all dependencies -->
<!-- you can exclude rest-assured from spring-cloud-contract-verifier -->
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<version>2.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>2.5.0</version>
<scope>test</scope>
</dependency>
</dependencies>
这样,插件会自动看到 Rest Assured 3.x 出现在 classpath 上,并相应地修改导入。
91.2.3 Maven 的快照版本
对于 Snapshot 和 Milestone 版本,您必须将以下部分添加到pom.xml
,如下所示:
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
91.2.4 添加存根
默认情况下,Spring Cloud Contract Verifier 正在查找src/test/resources/contracts
目录中的存根。包含存根定义的目录被视为 class name,每个存根定义被视为单个测试。我们假设它至少包含一个用作 test class name 的目录。如果有多个 level 的嵌套目录,则除最后一个之外的所有目录都将用作包 name。对于 example,具有以下结构:
src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy
Spring Cloud Contract Verifier 使用两种方法创建名为defaultBasePackage.MyService
的测试 class
-
shouldCreateUser()
-
shouldReturnUser()
91.2.5 Run 插件
插件目标generateTests
被指定在名为generate-test-sources
的阶段中调用。如果您希望它成为 build process 的一部分,则无需执行任何操作。如果您只想生成测试,请调用generateTests
目标。
91.2.6 配置插件
要更改默认的 configuration,只需在插件定义或execution
定义中添加configuration
部分,如下所示:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>convert</goal>
<goal>generateStubs</goal>
<goal>generateTests</goal>
</goals>
</execution>
</executions>
<configuration>
<basePackageForTests>org.springframework.cloud.verifier.twitter.place</basePackageForTests>
<baseClassForTests>org.springframework.cloud.verifier.twitter.place.BaseMockMvcSpec</baseClassForTests>
</configuration>
</plugin>
91.2.7 Configuration 选项
-
testMode:定义验收测试的模式。默认情况下,模式是 MockMvc,它基于 Spring 的 MockMvc。对于真正的 HTTP calls,它也可以更改为JaxRsClient或Explicit。
-
basePackageForTests:指定所有生成的测试的基础包。如果未设置,则从
baseClassForTests's package and from
packageWithBaseClasses. If neither of these values are set, then the value is set to
org.springframework.cloud.contract.verifier.tests`中选择 value。 -
ruleClassForTests:指定应添加到生成的测试 classes 的规则。
-
baseClassForTests:为所有生成的测试创建 base class。默认情况下,如果使用 Spock classes,则 class 为
spock.lang.Specification
。 -
contractsDirectory:指定包含用 GroovyDSL 编写的 contracts 的目录。默认目录是
/src/test/resources/contracts
。 -
testFramework:指定要使用的目标测试 framework。目前,支持 Spock 和 JUnit,JUnit 是默认的 framework
-
packageWithBaseClasses:定义一个包含所有 base classes 的包。此设置优先于baseClassForTests。约定是这样的,如果你有一个 contract(for example)
src/test/resources/contract/foo/bar/baz/
并将packageWithBaseClasses
property 的 value 设置为com.example.base
,那么 Spring Cloud Contract Verifier 假定com.example.base
包下有一个BarBazBase
class。换句话说,系统采用包的最后两部分(如果存在),并形成带有Base
后缀的 class。 -
baseClassMappings:指定提供
contractPackageRegex
的 base class 映射列表,该列表根据 contract 所在的包进行检查,baseClassFQN
,maps 到匹配 contract 的 base class 的完全限定 name。例如,如果src/test/resources/contract/foo/bar/baz/
下有 contract,property.* → com.example.base.BaseClass
有 map,那么从这些 contracts 生成的 test class 会扩展com.example.base.BaseClass
。此设置优先于packageWithBaseClasses和baseClassForTests。 -
contractsProperties:包含要传递给 Spring Cloud Contract 组件的 properties 的 map。那些 properties 可能被 e.g 使用。内置或自定义 Stub 下载器。
如果要从 Maven repository 下载 contract 定义,可以使用以下选项:
-
contractDependency:包含所有打包的 contracts 的 contract 依赖项。
-
contractsPath:带有 contracts 的 JAR 中具体 contracts 的路径。默认为
groupid/artifactid
,其中gropuid
是斜杠分隔。 -
contractsMode:选择将找到并注册存根的模式
-
contractsSnapshotCheckSkip:如果
true
则不会断言是否从本地或 remote 位置下载了 stub/_conract JAR -
deleteStubsAfterTest:如果设置为
false
,则不会从临时目录中删除任何已下载的 contracts -
contractsRepositoryUrl:带有 contracts 的 artifact 的 repo 的 URL。如果未提供,请使用当前的 Maven。
-
contractsRepositoryUsername:用于使用 contracts 连接到 repo 的用户 name。
-
contractsRepositoryPassword:用于通过 contracts 连接到 repo 的密码。
-
contractsRepositoryProxyHost:用于通过 contracts 连接到 repo 的代理 host。
-
contractsRepositoryProxyPort:用于通过 contracts 连接到 repo 的 proxy port。
我们只缓存 non-snapshot,显式提供的版本(对于 example +
或1.0.0.BUILD-SNAPSHOT
不会被缓存)。默认情况下,此 feature 已打开。
91.2.8 单个 Base Class for All Tests
在默认的 MockMvc 中使用 Spring Cloud Contract Verifier 时,您需要为所有生成的验收测试创建基本规范。在此 class 中,您需要指向应验证的端点。
package org.mycompany.tests
import org.mycompany.ExampleSpringController
import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc
import spock.lang.Specification
class MvcSpec extends Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(new ExampleSpringController())
}
}
如有必要,您还可以设置整个 context。
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.Before;
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;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")
public abstract class BaseTestClass {
@Autowired
WebApplicationContext context;
@Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(this.context);
}
}
如果使用EXPLICIT
模式,则可以使用 base class 类似地初始化整个测试的应用程序,就像在常规 integration 测试中找到的那样。
import io.restassured.RestAssured;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")
public abstract class BaseTestClass {
@LocalServerPort
int port;
@Before
public void setup() {
RestAssured.baseURI = "http://localhost:" + this.port;
}
}
如果使用JAXRSCLIENT
模式,则此 base class 也应包含protected WebTarget webTarget
字段。现在,测试 JAX-RS API 的唯一选择是启动 web 服务器。
91.2.9 contracts 的 base classes 不同
如果你的 base classes 在 contracts 之间有所不同,你可以告诉 Spring Cloud Contract 插件哪个 class 应该由自动生成的测试扩展。您有两种选择:
-
通过提供
packageWithBaseClasses
遵循惯例 -
通过
baseClassMappings
提供显式映射
按照惯例
约定是这样的,如果你有一个 contract(for example)src/test/resources/contract/foo/bar/baz/
并将packageWithBaseClasses
property 的 value 设置为com.example.base
,那么 Spring Cloud Contract Verifier 假定com.example.base
包下有一个BarBazBase
class。换句话说,系统采用包的最后两部分(如果存在),并形成带有Base
后缀的 class。此规则优先于baseClassForTests。这是闭包中它如何工作的一个例子:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<configuration>
<packageWithBaseClasses>hello</packageWithBaseClasses>
</configuration>
</plugin>
通过映射
您可以手动将 contract 包的正则表达式 map 映射到匹配的 contract 的 base class 的完全限定 name。您必须提供一个名为baseClassMappings
的列表,其中包含baseClassMapping
objects,它采用contractPackageRegex
到baseClassFQN
映射。考虑以下 example:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<configuration>
<baseClassForTests>com.example.FooBase</baseClassForTests>
<baseClassMappings>
<baseClassMapping>
<contractPackageRegex>.*com.*</contractPackageRegex>
<baseClassFQN>com.example.TestBase</baseClassFQN>
</baseClassMapping>
</baseClassMappings>
</configuration>
</plugin>
假设您在这两个位置下有 contracts:* src/test/resources/contract/com/
* src/test/resources/contract/foo/
通过提供baseClassForTests
,我们有一个后备,因为映射没有成功。 (你也可以提供packageWithBaseClasses
作为 fallback.)这样,从src/test/resources/contract/com/
contracts 生成的测试扩展com.example.ComBase
,而测试的 rest 扩展com.example.FooBase
。
91.2.10 调用生成的测试
Spring Cloud Contract Maven 插件在名为/generated-test-sources/contractVerifier
的目录中生成验证 code,并将此目录附加到testCompile
目标。
对于 Groovy Spock code,请使用以下命令:
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<testSources>
<testSource>
<directory>${project.basedir}/src/test/groovy</directory>
<includes>
<include>**/*.groovy</include>
</includes>
</testSource>
<testSource>
<directory>${project.build.directory}/generated-test-sources/contractVerifier</directory>
<includes>
<include>**/*.groovy</include>
</includes>
</testSource>
</testSources>
</configuration>
</plugin>
要确保提供者方符合已定义的 contracts,您需要调用mvn generateTest test
。
91.2.11 将存根推送到 SCM
如果您正在使用 SCM repository 来保留 contracts 和 stubs,那么您可能希望自动执行将存根推送到 repository 的步骤。要做到这一点,添加pushStubsToScm
目标就足够了。 例:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<!-- Base class mappings etc. -->
<!-- We want to pick contracts from a Git repository -->
<contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>
<!-- We reuse the contract dependency section to set up the path
to the folder that contains the contract definitions. In our case the
path will be /groupId/artifactId/version/contracts -->
<contractDependency>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</contractDependency>
<!-- The contracts mode can't be classpath -->
<contractsMode>REMOTE</contractsMode>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<!-- By default we will not push the stubs back to SCM,
you have to explicitly add it as a goal -->
<goal>pushStubsToScm</goal>
</goals>
</execution>
</executions>
</plugin>
在第 97.6 节,“使用 SCM Stub Downloader”下,您可以找到所有可以通过<configuration><contractProperties>
map,系统 property 或环境变量传递的 configuration 选项。
91.2.12 Maven 插件和 STS
如果在使用 STS 时看到以下 exception:
当您单击错误标记时,您应该看到如下内容:
plugin:1.1.0.M1:convert:default-convert:process-test-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
cloud-contract-maven-plugin:1.1.0.M1:convert failed. at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145) at
org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:331) at org.eclipse.m2e.core.internal.embedder.MavenImpl$11.call(MavenImpl.java:1362) at
...
org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) Caused by: java.lang.NullPointerException at
org.eclipse.m2e.core.internal.builder.plexusbuildapi.EclipseIncrementalBuildContext.hasDelta(EclipseIncrementalBuildContext.java:53) at
org.sonatype.plexus.build.incremental.ThreadBuildContext.hasDelta(ThreadBuildContext.java:59) at
要解决此问题,请在pom.xml
中提供以下部分:
<build>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<versionRange>[1.0,)</versionRange>
<goals>
<goal>convert</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
91.3 存根和传递依赖关系
Maven 和 Gradle 插件,为您添加创建存根 jar 的任务。出现的一个问题是,在重用存根时,您可能会错误地 import 所有存根的依赖关系。当 building a Maven artifact 时,即使你有几个不同的 jars,它们都共享一个 pom:
├── github-webhook-0.0.1.BUILD-20160903.075506-1-stubs.jar
├── github-webhook-0.0.1.BUILD-20160903.075506-1-stubs.jar.sha1
├── github-webhook-0.0.1.BUILD-20160903.075655-2-stubs.jar
├── github-webhook-0.0.1.BUILD-20160903.075655-2-stubs.jar.sha1
├── github-webhook-0.0.1.BUILD-SNAPSHOT.jar
├── github-webhook-0.0.1.BUILD-SNAPSHOT.pom
├── github-webhook-0.0.1.BUILD-SNAPSHOT-stubs.jar
├── ...
└── ...
使用这些依赖项有三种可能性,以免对传递依赖项产生任何问题:
-
将所有 application 依赖项标记为可选
-
为存根创建单独的 artifactid
-
排除 consumer 方面的依赖关系
将所有 application 依赖项标记为可选
如果在github-webhook
application 中将所有依赖项标记为可选,则在另一个 application 中包含github-webhook
存根时(或者当 Stub Runner 下载该依赖项时),因为所有依赖项都是可选的,它们将无法获取下载。
为存根创建单独的 artifactid
如果您创建一个单独的artifactid
,那么您可以按照您希望的任何方式进行设置。例如,您可能决定根本没有依赖关系。
排除 consumer 方面的依赖关系
作为 consumer,如果将存根依赖项添加到 classpath,则可以显式排除不需要的依赖项。
91.4 CI 服务器设置
在 CI,共享环境中获取存根/ contracts 时,可能会发生的情况是 producer 和 consumer 都重用相同的本地 Maven repository。因此,负责从 remote 位置下载存根 JAR 的 framework 无法决定应该选择哪个 JAR,本地还是 remote。这导致"The artifact was found in the local repository but you have explicitly stated that it should be downloaded from a remote one"
exception 并使 build 失败。
对于这种情况,我们引入了 property 和插件设置机制:
-
通过
stubrunner.snapshot-check-skip
system property -
通过
STUBRUNNER_SNAPSHOT_CHECK_SKIP
环境变量
如果这些值中的任何一个设置为true
,那么存根下载器将不会验证下载的 JAR 的来源。
对于插件,您需要将contractsSnapshotCheckSkip
property 设置为true
。
91.5 场景
您可以使用 Spring Cloud Contract Verifier 处理方案。您需要做的就是在创建 contracts 时坚持正确的命名约定。该约定要求包含 order 数字后跟下划线。这将考虑您是否使用 YAML 或 Groovy。 例:
my_contracts_dir\
scenario1\
1_login.groovy
2_showCart.groovy
3_logout.groovy
这样的树导致 Spring Cloud Contract Verifier 生成具有 name scenario1
的 WireMock 场景以及以下三个步骤:
-
登录标记为
Started
指向... -
showCart 标记为
Step1
指向... -
注销标记为
Step2
,将关闭方案。
有关 WireMock 方案的更多详细信息,请访问http://wiremock.org/docs/stateful-behaviour/
Spring Cloud Contract Verifier 还会生成具有保证执行顺序的测试。
91.6 Docker 项目
我们发布了一个springcloud/spring-cloud-contract
Docker 镜像,其中包含一个项目,该项目将生成测试并在EXPLICIT
模式下针对 running application 执行它们。
EXPLICIT
模式意味着从 contracts 生成的测试将发送实际请求而不是模拟的请求。
91.6.1 Maven,JARs 和二进制存储的简介
由于非 JVM 项目可以使用 Docker 镜像,因此最好解释 Spring Cloud Contract 打包默认设置背后的基本术语。
部分以下定义取自Maven 词汇表
-
Project
:Maven 从项目角度思考。你将 build 的所有东西都是项目。这些项目遵循明确定义的“Project Object Model”。项目可以依赖于其他项目,在这种情况下,后者称为“依赖项”。一个项目可能与几个子项目一致,但这些子项目仍然被视为项目。 -
Artifact
:artifact 是由项目生成或使用的东西。 Maven 为项目生成的 artifacts 示例包括:JARs,源代码和二进制分发。每个 artifact 都由 group id 和 artifact ID 唯一标识,该 ID 在 group 中是唯一的。 -
JAR
:JAR 代表 Java ARchive。这是一种基于 ZIP 文件格式的格式。 Spring Cloud Contract 将 contracts 和生成的存根打包在 JAR 文件中。 -
GroupId
:group ID 是项目的通用唯一标识符。虽然这通常只是项目 name(例如 commons-collections),但使用 fully-qualified package name 将其与具有类似 name(例如 org.apache.maven)的其他项目区分开是有帮助的。通常,当发布到 Artifact Manager 时,GroupId
将被斜杠分隔并形成 URL 的一部分。 E.g。对于 group idcom.example
和 artifact idapplication
将是/com/example/application/
。 -
Classifier
:Maven 依赖表示法如下所示:groupId:artifactId:version:classifier
。分类器是传递给依赖项的附加后缀。 E.g。stubs
,sources
。相同的依赖 e.g.com.example:application
可以使用分类器生成多个彼此不同的 artifact。 -
Artifact manager
:当您生成二进制文件/源/包时,您希望其他人可以下载/ reference 或重用它们。在 JVM 世界的情况下,那些 artifacts 将是 JARs,对于 Ruby 来说这些是宝石,而对于 Docker 来说,那些将是 Docker 镜像。您可以在 manager 中存储这些 artifacts。这种 managers 的例子可以是Artifactory或关系。
91.6.2 它是如何工作
图像在/contracts
文件夹下搜索 contracts。 running 测试的输出将在/spring-cloud-contract/build
文件夹下提供(它对调试很有用)。
它足以让您挂载 contracts,传递环境变量,图像将:
-
生成 contract 测试
-
根据提供的 URL 执行测试
-
生成WireMock存根
-
(可选 - 默认情况下处于启用状态)将存根发布到 Artifact Manager
环境变量
Docker 镜像需要一些环境变量来指向__unning application,指向 Artifact manager 实例等。
-
PROJECT_GROUP
- 您项目的 group id。默认为com.example
-
PROJECT_VERSION
- 您项目的 version。默认为0.0.1-SNAPSHOT
-
PROJECT_NAME
- artifact id。默认为example
-
REPO_WITH_BINARIES_URL
- Artifact Manager 的 URL。默认为http://localhost:8081/artifactory/libs-release-local
,这是Artifactory running 本地的默认 URL -
当 Artifact Manager 受到保护时
REPO_WITH_BINARIES_USERNAME
- (可选)用户名 -
当 Artifact Manager 受到保护时
REPO_WITH_BINARIES_PASSWORD
- (可选)密码 -
PUBLISH_ARTIFACTS
- 如果设置为true
,则会将 artifact 发布到二进制存储。默认为true
。
当 contracts 位于外部 repository 中时,将使用这些环境变量。要启用此 feature,您必须设置EXTERNAL_CONTRACTS_ARTIFACT_ID
环境变量。
-
EXTERNAL_CONTRACTS_GROUP_ID
- 带有 contracts 的项目的 group id。默认为com.example
-
EXTERNAL_CONTRACTS_ARTIFACT_ID
- 带有 contracts 的项目的 artifact id。 -
EXTERNAL_CONTRACTS_CLASSIFIER
- 带有 contracts 的项目分类器。默认为空 -
EXTERNAL_CONTRACTS_VERSION
- 带有 contracts 的项目 version。默认为+
,相当于选择最新 -
EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL
- Artifact Manager 的 URL。默认为REPO_WITH_BINARIES_URL
env var 的 value。如果未设置,则默认为http://localhost:8081/artifactory/libs-release-local
,这是Artifactory running 本地的默认 URL -
EXTERNAL_CONTRACTS_PATH
- 带有 contracts 的项目内给定项目的 contracts 路径。默认为斜杠分隔EXTERNAL_CONTRACTS_GROUP_ID
与/
和EXTERNAL_CONTRACTS_ARTIFACT_ID
连接。 E.g。对于 group idfoo.bar
和 artifact idbaz
,将导致foo/bar/baz
contracts 路径。 -
EXTERNAL_CONTRACTS_WORK_OFFLINE
- 如果设置为true
,则将从容器的.m2
中使用 contracts 检索 artifact。将本地.m2
挂载为容器/root/.m2
路径上的可用卷。您不能同时设置EXTERNAL_CONTRACTS_WORK_OFFLINE
和EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL
。
执行测试时使用这些环境变量:
-
APPLICATION_BASE_URL
- 应该执行测试的 url。请记住,必须可以从 Docker 容器访问它(e.g.localhost
将无效) -
APPLICATION_USERNAME
- (可选)用于对 application 进行基本身份验证的用户名 -
APPLICATION_PASSWORD
- (可选)用于 application 基本身份验证的密码
91.6.3 使用示例
我们来看一个简单的 MVC application
$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore
contracts 在/contracts
文件夹下可用。
91.6.4 服务器端(nodejs)
由于我们想要 run 测试,我们可以执行:
$ npm test
但是,出于学习目的,我们将它分成几部分:
# Stop docker infra (nodejs, artifactory)
$ ./stop_infra.sh
# Start docker infra (nodejs, artifactory)
$ ./setup_infra.sh
# Kill & Run app
$ pkill -f "node app"
$ nohup node app &
# Prepare environment variables
$ SC_CONTRACT_DOCKER_VERSION="..."
$ APP_IP="192.168.0.100"
$ APP_PORT="3000"
$ ARTIFACTORY_PORT="8081"
$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
$ CURRENT_DIR="$( pwd )"
$ CURRENT_FOLDER_NAME=${PWD##*/}
$ PROJECT_VERSION="0.0.1.RELEASE"
# Execute contract tests
$ docker run --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
# Kill app
$ pkill -f "node app"
会发生什么是通过 bash 脚本:
-
将建立基础设施(MongoDb,Artifactory)。在现实生活中,您只需使用模拟数据库运行 NodeJS application。在这个 example 中,我们想要展示如何在 time 中从 Spring Cloud Contract 中受益。
-
由于这些限制,contracts 也代表了有状态
-
第一个请求是
POST
,导致数据插入数据库 -
第二个请求是
GET
,它返回一个带有 1 个先前插入元素的数据列表 -
NodeJS application 将启动(在 port
3000
上) -
contract 测试将通过 Docker 生成,测试将针对 running application 执行
-
contracts 将取自
/contracts
文件夹。 -
测试执行的输出在
node_modules/spring-cloud-contract/output
下可用。 -
存根将上传到 Artifactory。你可以在http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/下查看它们。存根将在这里http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar。
要查看 client 端的外观,请查看第 93.9 节,“Stub Runner Docker”部分。