89. Spring Cloud Contract Verifier 设置

您可以通过以下方式设置 Spring Cloud Contract Verifier:

89.1 Gradle 项目

要了解如何为 Spring Cloud Contract Verifier 设置 Gradle 项目,请阅读以下部分:

89.1.1 Prerequisites

为了将 Spring Cloud Contract Verifier 与 WireMock 一起使用,您必须使用 Gradle 或 Maven 插件。

Warning

如果要在项目中使用 Spock,则必须分别添加spock-corespock-spring模块。检查Spock 文档以获取更多信息

89.1.2 添加具有依赖性的 Gradle 插件

要添加具有依赖性的 Gradle 插件,请使用类似于以下代码:

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'
}

89.1.3 Gradle and 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"
}

这样,该插件会自动看到 Classpath 中存在 Rest Assured 2.x,并相应地修改了导入。

89.1.4 Gradle 的快照版本

将其他快照存储库添加到 build.gradle 以使用快照版本,快照版本在每次成功构建后都会自动上传,如下所示:

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" }
	}
}

89.1.5 添加存根

默认情况下,Spring Cloud Contract Verifier 在src/test/resources/contracts目录中寻找存根。

包含存根定义的目录被视为类名,每个存根定义均被视为单个测试。 Spring Cloud Contract Verifier 假定它至少包含至少一层用作测试类名称的目录。如果存在多个嵌套目录,则使用除最后一个嵌套目录以外的所有目录作为包名。例如,具有以下结构:

src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy

Spring Cloud Contract Verifier 使用两种方法创建名为defaultBasePackage.MyService的测试类:

89.1.6 运行插件

该插件会注册自己,以在check任务之前被调用。如果您希望它成为构建过程的一部分,则无需执行其他任何操作。如果您只想生成测试,请调用generateContractTests任务。

89.1.7 默认设置

默认的 Gradle 插件设置会创建构建的以下 Gradle 部分(以伪代码):

contracts {
    testFramework ='JUNIT'
    testMode = 'MockMvc'
    generatedTestSourcesDir = project.file("${project.buildDir}/generated-test-sources/contracts")
    generatedTestResourcesDir = project.file("${project.buildDir}/generated-test-resources/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
        }
    }
}

89.1.8 配置插件

要更改默认配置,请在您的 Gradle 配置中添加一个contracts代码段,如下所示:

contracts {
	testMode = 'MockMvc'
	baseClassForTests = 'org.mycompany.tests'
	generatedTestSourcesDir = project.file('src/generatedContract')
}

89.1.9 配置选项

当您要指定包含 Contract 的 JAR 的位置时,使用以下属性:

您可以在下面找到通过插件打开的实验功能列表:

89.1.10 所有测试的单一 Base Class

在默认的 MockMvc 中使用 Spring Cloud Contract Verifier 时,您需要为所有生成的验收测试创建基本规范。在此类中,您需要指向一个端点,该端点应进行验证。

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 来初始化整个测试的应用程序,就像在常规集成测试中看到的那样。如果使用JAXRSCLIENT模式,则此 Base Class 也应包含protected WebTarget webTarget字段。现在,测试 JAX-RS API 的唯一选择是启动 Web 服务器。

89.1.11Contract 的不同基本类别

如果 Contract 之间的 Base Class 不同,则可以告诉 Spring Cloud Contract 插件应通过自动生成的测试扩展哪个类。您有两种选择:

By Convention

约定是这样的:如果您在(例如)src/test/resources/contract/foo/bar/baz/下有一个 Contract,并且将packageWithBaseClasses属性的值设置为com.example.base,那么 Spring Cloud Contract Verifier 会假定com.example.base包下有一个BarBazBase类。换句话说,系统将获取包的最后两个部分(如果存在的话),并形成带有Base后缀的类。此规则优先于 baseClassForTests 。以下是contracts闭包中的工作方式示例:

packageWithBaseClasses = 'com.example.base'

By Mapping

您可以将 Contract 包的正则表达式手动 Map 到匹配 Contract 的 Base Class 的完全限定名称。您必须提供一个名为baseClassMappings的列表,该列表由baseClassMapping个对象组成,这些对象采用contractPackageRegexbaseClassFQN的 Map。考虑以下示例:

baseClassForTests = "com.example.FooBase"
baseClassMappings {
	baseClassMapping('.*/com/.*', 'com.example.ComBase')
	baseClassMapping('.*/bar/.*':'com.example.BarBase')
}

假设您有-src/test/resources/contract/com/-src/test/resources/contract/foo/的 Contract

通过提供baseClassForTests,我们可以进行后备,以防 Map 不成功。 (您也可以提供packageWithBaseClasses作为后备.)这样,从src/test/resources/contract/com/Contract 生成的测试扩展了com.example.ComBase,而其余测试扩展了com.example.FooBase

89.1.12 调用生成的测试

为确保提供方符合已定义的 Contract,您需要调用:

./gradlew generateContractTests test

89.1.13 将存根推送到 SCM

如果您使用 SCM 存储库保留 Contract 和存根,则可能需要自动化将存根推入存储库的步骤。为此,只需调用pushStubsToScm任务即可。例:

$ ./gradlew pushStubsToScm

第 95.6 节“使用 SCM 存根下载器”下,您可以找到可以通过contractsProperties字段传递的所有可能的配置选项,例如contracts { contractsProperties = [foo:"bar"] },通过contractsProperties方法,例如contracts { contractsProperties([foo:"bar"]) },系统属性或环境变量。

Consumer 方的 Spring CloudContract 验证程序 89.1.14

在消费服务中,您需要以与提供者相同的方式配置 Spring Cloud Contract Verifier 插件。如果您不想使用 Stub Runner,则需要复制存储在src/test/resources/contracts中的 Contract,并使用以下方法生成 WireMock JSON 存根:

./gradlew generateClientStubs

Note

必须设置stubsOutputDir选项,才能生成存根。

如果存在,JSON 存根可用于使用服务的自动化测试中。

@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服务。由 WireMock 服务器处理此请求,该服务器配置有 Spring Cloud Contract Verifier 生成的存根。

89.2 Maven 项目

要了解如何为 Spring Cloud Contract Verifier 设置 Maven 项目,请阅读以下部分:

89.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>
		<convertToYaml>true</convertToYaml>
	</configuration>
</plugin>

您可以在Spring Cloud Contract Maven 插件文档(2.0.0.RELEASE 版本的示例)中阅读更多内容。

89.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>

这样,该插件会自动看到 Classpath 中存在 Rest Assured 3.x,并相应地修改了导入。

89.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>

89.2.4 添加存根

默认情况下,Spring Cloud Contract Verifier 在src/test/resources/contracts目录中寻找存根。包含存根定义的目录被视为类名,每个存根定义均被视为单个测试。我们假定它至少包含一个目录用作测试类名称。如果嵌套目录有多个级别,则将最后一个嵌套目录用作包名称。例如,具有以下结构:

src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy

Spring Cloud Contract Verifier 使用两种方法创建名为defaultBasePackage.MyService的测试类

89.2.5 运行插件

插件目标generateTests被分配为在名为generate-test-sources的阶段中被调用。如果您希望它成为构建过程的一部分,则无需执行任何操作。如果只想生成测试,请调用generateTests目标。

89.2.6 配置插件

要更改默认配置,只需在插件定义或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>

89.2.7 配置选项

如果要从 Maven 存储库下载 Contract 定义,则可以使用以下选项:

我们仅缓存非快照的显式提供的版本(例如+1.0.0.BUILD-SNAPSHOT将不会被缓存)。默认情况下,此功能处于打开状态。

您可以在下面找到通过插件打开的实验功能列表:

89.2.8 适用于所有测试的单一 Base Class

在默认的 MockMvc 中使用 Spring Cloud Contract Verifier 时,您需要为所有生成的验收测试创建基本规范。在此类中,您需要指向一个端点,该端点应进行验证。

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())
  }
}

如果需要,您还可以设置整个上下文。

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 类似地初始化整个测试的应用程序,就像在常规集成测试中可能会发现的那样。

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 服务器。

89.2.9Contract 的不同 Base Class

如果 Contract 之间的 Base Class 不同,则可以告诉 Spring Cloud Contract 插件应通过自动生成的测试扩展哪个类。您有两种选择:

By Convention

约定是这样的:如果您在(例如)src/test/resources/contract/foo/bar/baz/下有一个 Contract,并且将packageWithBaseClasses属性的值设置为com.example.base,那么 Spring Cloud Contract Verifier 会假定com.example.base包下有一个BarBazBase类。换句话说,系统将获取包的最后两个部分(如果存在的话),并形成带有Base后缀的类。此规则优先于 baseClassForTests 。以下是contracts闭包中的工作方式示例:

<plugin>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
	<configuration>
		<packageWithBaseClasses>hello</packageWithBaseClasses>
	</configuration>
</plugin>

By Mapping

您可以将 Contract 包的正则表达式手动 Map 到匹配 Contract 的 Base Class 的完全限定名称。您必须提供一个名为baseClassMappings的列表,该列表由baseClassMapping个对象组成,这些对象采用contractPackageRegexbaseClassFQN的 Map。考虑以下示例:

<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>

假设您在以下两个位置拥有 Contract:* src/test/resources/contract/com/ * src/test/resources/contract/foo/

通过提供baseClassForTests,我们可以进行后备,以防 Map 不成功。 (您也可以提供packageWithBaseClasses作为后备.)这样,从src/test/resources/contract/com/Contract 生成的测试扩展了com.example.ComBase,而其余测试扩展了com.example.FooBase

89.2.10 调用生成的测试

Spring Cloud Contract Maven 插件在名为/generated-test-sources/contractVerifier的目录中生成验证代码,并将此目录附加到testCompile目标。

对于 Groovy Spock 代码,请使用以下代码:

<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>

为了确保提供方符合已定义的 Contract,您需要调用mvn generateTest test

89.2.11 将存根推送到 SCM

如果您使用 SCM 存储库保留 Contract 和存根,则可能需要自动化将存根推入存储库的步骤。为此,添加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>

第 95.6 节“使用 SCM 存根下载器”下,您可以找到可以通过<configuration><contractProperties>Map,系统属性或环境变量传递的所有可能的配置选项。

89.2.12 Maven 插件和 STS

如果在使用 STS 时看到以下异常:

当您单击错误标记时,您应该看到类似以下的内容:

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>

89.2.13 具有 Spock 测试的 Maven 插件

您可以选择Spock Framework来使用 Maven 和 Gradle 插件创建和执行自动生成的 Contract 验证测试。但是,尽管使用 Gradle 非常简单,但是在 Maven 中,您将需要一些其他设置才能使测试正确编译和执行。

首先,您将必须使用一个插件(例如GMavenPlus插件)将 Groovy 添加到您的项目中。在 GMavenPlus 插件中,您将需要显式设置测试源,包括定义基本测试类的路径和添加了生成的 Contract 测试的路径。请参考以下示例:

<plugin>
    <groupId>org.codehaus.gmavenplus</groupId>
    <artifactId>gmavenplus-plugin</artifactId>
    <version>1.6.1</version>
    <executions>
        <execution>
            <goals>
                <goal>compileTests</goal>
                <goal>addTestSources</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <testSources>
            <testSource>
                <directory>${project.basedir}/src/test/groovy</directory>
                <includes>
                    <include>**/*.groovy</include>
                </includes>
            </testSource>
            <testSource>
                <directory>
                    ${project.basedir}/target/generated-test-sources/contracts/com/example/beer
                </directory>
                <includes>
                    <include>**/*.groovy</include>
                    <include>**/*.gvy</include>
                </includes>
            </testSource>
        </testSources>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.15</version>
            <scope>runtime</scope>
            <type>pom</type>
        </dependency>
    </dependencies>

如果您坚持使用Spec结束测试类名称的 Spock 约定,则还需要调整 Maven Surefire 插件设置,如以下示例所示:


89.3 存根和传递依赖项

Maven 和 Gradle 插件可添加为您创建存根 jar 的任务。出现的一个问题是,当重用存根时,您可能会错误地导入该存根的所有依赖关系。在构建 Maven 工件时,即使您有几个不同的 jar,它们都共享一个 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
├── ...
└── ...

使用这些依赖项有三种可能性,以使传递依赖项没有任何问题:

将所有应用程序依赖项标记为可选

如果在github-webhook应用程序中将所有依赖项标记为可选,则在另一个应用程序中包含github-webhook存根时(或当 Stub Runner 下载该依赖项时),则由于所有依赖项都是可选的,因此它们将不会得到已下载。

为存根创建单独的artifactid

如果您创建单独的artifactid,则可以按照您希望的任何方式进行设置。例如,您可能决定完全没有依赖项。

排除 Consumer 方面的依赖

作为使用者,如果将存根依赖项添加到 Classpath 中,则可以显式排除不需要的依赖项。

89.4 Scenarios

您可以使用 Spring Cloud Contract Verifier 处理方案。您需要做的就是在创建 Contract 时遵循正确的命名约定。约定要求在订货号后加上下划线。无论您使用的是 YAML 还是 Groovy,这都将起作用。例:

my_contracts_dir\
  scenario1\
    1_login.groovy
    2_showCart.groovy
    3_logout.groovy

这样的树使 Spring Cloud Contract Verifier 生成名称为scenario1的 WireMock 场景,并执行以下三个步骤:

有关 WireMock 方案的更多详细信息,请参见http://wiremock.org/docs/stateful-behaviour/

Spring Cloud Contract Verifier 还会生成具有保证执行 Sequences 的测试。

89.5 Docker 项目

我们正在发布springcloud/spring-cloud-contract Docker 映像,其中包含一个项目,该项目将生成测试并针对正在运行的应用程序以EXPLICIT模式执行测试。

Tip

EXPLICIT模式意味着从 Contract 生成的测试将发送真实的请求,而不是模拟的请求。

89.5.1 Maven,JAR 和二进制存储的简短介绍

由于 Docker 映像可由非 JVM 项目使用,因此最好解释一下 Spring Cloud Contract 打包默认设置背后的基本术语。

以下定义的一部分取自Maven Glossary

89.5.2 工作原理

该图像在/contracts文件夹下搜索 Contract。运行测试的输出将在/spring-cloud-contract/build文件夹下提供(对于调试目的很有用)。

挂载 Contract,传递环境变量就足够了,该映像将:

Environment Variables

Docker 映像需要一些环境变量以指向您正在运行的应用程序,工件 Management 器实例等。

当 Contract 位于外部存储库中时,将使用这些环境变量。要启用此功能,必须设置EXTERNAL_CONTRACTS_ARTIFACT_ID环境变量。

执行测试时使用以下环境变量:

89.5.3 使用示例

让我们看一个简单的 MVC 应用程序

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

Contract 位于/contracts文件夹下。

89.5.4 服务器端(nodejs)

因为我们要运行测试,所以我们可以执行:

$ 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 脚本:

要查看 Client 端的外观,请查看第 91.9 节“ Stub Runner Docker”部分。

首页