Maven Project

Adding the Maven Plugin

若要添加 Spring Cloud Contract BOM,在您的 pom.xml 文件中包含以下部分:

Unresolved directive in maven-project.adoc - include::{standalone_samples_path}/http-server/pom.xml[]

接下来,按以下方式添加 Spring Cloud Contract Verifier Maven 插件:

Unresolved directive in maven-project.adoc - include::{standalone_samples_path}/http-server/pom.xml[]

您可以在spring-cloud-contract-maven-plugin/index.html[Spring Cloud Contract Maven 插件文档]中阅读更多内容。

有时,无论选择什么 IDE,您都可以看到 target/generated-test-source 文件夹在 IDE 的类路径中不可见。为确保它始终存在,您可以向您的 pom.xml 添加以下条目

<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>build-helper-maven-plugin</artifactId>
	<executions>
		<execution>
			<id>add-source</id>
			<phase>generate-test-sources</phase>
			<goals>
				<goal>add-test-source</goal>
			</goals>
			<configuration>
				<sources>
					<source>${project.build.directory}/generated-test-sources/contracts/</source>
				</sources>
			</configuration>
		</execution>
	</executions>
</plugin>

Maven and Rest Assured 2.0

默认情况下,Rest Assured 3.x 被添加到类路径。但是,您可以通过以下方式将 RestAssured 2.x 添加到插件类路径来使用它:

<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 2.x 在类路径中,并相应地修改导入。

Using Snapshot and Milestone Versions for Maven

若要使用 Snapshot 和 Milestone 版本,您必须将以下部分添加到您的 pom.xml

Unresolved directive in maven-project.adoc - include::{standalone_samples_path}/http-server/pom.xml[]

Adding stubs

默认情况下,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 的测试类,其中包含两个方法:

  • shouldCreateUser()

  • shouldReturnUser()

Run Plugin

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

如果您希望从 Maven 运行存根,请调用使用存根作为 spring.cloud.contract.verifier.stubs 系统属性运行的 run 目标,如下所示:

mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:run \-Dspring.cloud.contract.verifier.stubs="com.acme:service-name"

Configure plugin

若要更改默认配置,您可以向插件定义或 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>

Configuration Options

  • testMode: 为验收测试定义模式。默认情况下,该模式为 MockMvc,此模式基于 Spring 的 MockMvc。您还可以将其更改为 WebTestClientJaxRsClientExplicit(对于真正的 HTTP 调用)。

  • basePackageForTests: 指定所有生成测试的基础包。如果没有设置,此值将从 baseClassForTests 的包和 packageWithBaseClasses 的包中选取。如果这两个值都没有设置,则会将此值设置为 org.springframework.cloud.contract.verifier.tests

  • ruleClassForTests: 指定应添加到生成测试类中的规则。

  • baseClassForTests: 创建所有生成测试的基础类。默认情况下,如果您使用 Spock 类,这个类为 spock.lang.Specification

  • contractsDirectory: 指定包含使用 Groovy DSL 编写的合同的目录。默认目录为 /src/test/resources/contracts

  • generatedTestSourcesDir: 指定生成自 Groovy DSL 的测试应放置在其中的测试源目录。默认情况下,它的值为 $buildDir/generated-test-sources/contracts

  • generatedTestResourcesDir: 指定由生成测试使用的资源的测试资源目录。

  • testFramework: 指定要使用的目标测试框架。当前支持 Spock、JUnit 4 (TestFramework.JUNIT) 和 JUnit 5,而 JUnit 4 为默认框架。

  • packageWithBaseClasses: 定义所有基础类驻留的包。此设置优先于 baseClassForTests。约定如下:如果您有一个合同(例如)位于 src/test/resources/contract/foo/bar/baz/ 下并设置了 packageWithBaseClasses 属性的值为 com.example.base,则 Spring Cloud Contract Verifier 假定在 com.example.base 包下有一个 BarBazBase 类。换句话说,系统采用包的最后两个部分(如果存在),并使用 Base 作为后缀来形成一个类。

  • baseClassMappings: 指定提供 contractPackageRegex(根据合同所在的包进行检查)和 baseClassFQN(映射到匹配合同的基础类的完全限定名称)的基础类映射列表。例如,如果您有一个合同位于 src/test/resources/contract/foo/bar/baz/ 下并映射了 .* &#8594; com.example.base.BaseClass 属性,则从这些合同生成的测试类会扩展 com.example.base.BaseClass。此设置优先于 packageWithBaseClassesbaseClassForTests

  • contractsProperties:包含要传递给 Spring Cloud Contract 组件的属性的地图。这些属性可以被(例如)内置的或自定义的 Stub Downloader 使用。

  • failOnNoContracts:启用后,如果没有找到合约时将抛出异常。默认为 true

  • failOnInProgress:如果设置为 true,则在发现正在进行的合约时中止构建。在生产者方面,你需要明确正在进行的合约,并考虑可能会在消费者端导致错误的测试结果。默认为 true

  • incrementalContractTests:启用后,仅在自上次构建以来合约发生更改时创建测试。默认为 true

  • incrementalContractStubs: 启用后,仅当自上次构建以来合同发生更改时才创建存根。默认为 true

  • incrementalContractStubsJar: 启用后,仅当自上次构建以来存根发生更改时才创建存根 jar。默认为 truehttpPort :提供存根的 WireMock 服务器的 HTTP 端口。当前 spring.cloud.contract.verifier.http.port 属性仅在从目录提供存根时有效。否则,在提供存根 ID 时,端口必须包含在 ID 字符串中。skip: 将其设置为 true 以绕过验证器执行。skipTestOnly: 将其设置为 true 以绕过验证器测试生成。stubs :要下载并使用冒号分隔的 Ivy 符号运行的存根列表。minPort :指定存根应开始的最小端口。maxPort :指定存根应开始的最大端口。waitForKeyPressed :指定插件在启动存根后是否应该等待用户按压键。stubsClassifier: 指定存根工件使用的分类。

如果您希望从 Maven 仓库下载您的契约定义,您可以使用以下选项:

  • contractDependency: 包含所有打包合同的合约依赖项。

  • contractsPath: JAR 中包含打包合同的具体合同的路径。默认为 groupid/artifactid ,其中 gropuid 用斜杠分隔。

  • contractsMode: 选择查找和注册存根的模式。

  • deleteStubsAfterTest: 如果设置为 false,请不要从临时目录中删除任何下载的合同。

  • contractsRepositoryUrl: 拥有具有合同的工件的存储库的 URL。如果未提供,请使用当前的 Maven。

  • contractsRepositoryUsername: 用于连接到拥有合同的存储库的用户名。

  • contractsRepositoryPassword: 用于连接到拥有合同的存储库的密码。

  • contractsRepositoryProxyHost: 用于连接到拥有合同的存储库的代理主机。

  • contractsRepositoryProxyPort: 用于连接到拥有合同的存储库的代理端口。

我们仅缓存非快照、明确提供的版本(例如,+1.0.0.BUILD-SNAPSHOT 不会被缓存)。默认情况下,此功能被启用。

以下列表描述了您可以在插件中启用的试验性功能:

  • convertToYaml: 将所有 DSL 转换为声明式 YAML 格式。当你在 Groovy DSL 中使用外部库时,这可能非常有用。通过开启此功能(将其设置为 true),你无需在使用者端添加库依赖项。

  • assertJsonSize: 你可以在生成的测试中查看 JSON 数组的大小。此功能默认禁用。

Single Base Class for All Tests

当在默认(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 模式,您可以使用基本类来初始化整个被测试应用,类似于您在常规集成测试中的做法。以下示例演示如何执行此操作:

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 模式,则此基本类还应该包含 受保护的 WebTarget webTarget 字段。目前,测试 JAX-RS API 的唯一方法是启动 Web 服务器。

Using Different Base Classes for Contracts

如果您的基本类在契约之间有所不同,您可以告诉 Spring Cloud Contract 插件哪个类应该被自动生成的测试扩展。您有两种选择:

  • 按照惯例为 packageWithBaseClasses 提供值

  • 使用 baseClassMappings 提供显式映射

By Convention

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

Unresolved directive in maven-project.adoc - include::{plugins_path}/spring-cloud-contract-maven-plugin/src/test/projects/basic-generated-baseclass/pom.xml[]

By Mapping

您可以手动将契约包的正则表达式映射到匹配契约的基础类的完全限定名。您必须提供一个名为 baseClassMappings 的列表,该列表由 baseClassMapping 对象组成,每个对象采用 contractPackageRegexbaseClassFQN 映射。考虑以下示例:

Unresolved directive in maven-project.adoc - include::{plugins_path}/spring-cloud-contract-maven-plugin/src/test/projects/basic-baseclass-from-mappings/pom.xml[]

假设您在这两个位置有契约:

  • src/test/resources/contract/com/

  • src/test/resources/contract/foo/

通过提供 baseClassForTests,我们可以在映射未成功的情况下的预备(您还可以提供 packageWithBaseClasses 作为预备)。这样,从 src/test/resources/contract/com/ 契约生成的测试会扩展 com.example.ComBase,而其余测试会扩展 com.example.FooBase

Invoking Generated Tests

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>

要确保提供者端符合定义的契约,您需要调用 mvn generateTest 测试。

Pushing Stubs to SCM

如果您使用 SCM(源代码管理)存储库来保留契约和存根,您可能需要将将存根推送到存储库的步骤自动化。为此,您可以添加 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>

Using the SCM Stub Downloader中,您可以找到可以通过`<configuration><contractsProperties>`映射、系统属性或环境变量传递的所有可能的配置选项。例如,您可以指定一个具体的branch来签出,而不是默认branch。

<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>
	<contractsProperties>
            <git.branch>another_branch</git.branch>
        </contractsProperties>

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

Maven Plugin and STS

下图显示了在使用 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>

Maven Plugin with Spock Tests

您可以选择 Spock Framework使用Maven和Gradle创建和运行自动生成的合同验证测试。然而,在使用Gradle时很简单,而在Maven中,您需要一些额外的设置才能使测试得到正确编译和执行。

首先,您必须使用一个插件,例如 GMavenPlus插件, 将Groovy添加到您的项目中。在GMavenPlus插件中,您需要明确设置测试源,包括base测试类被定义的路径以及生成合同测试被添加的路径。{samples_url}/producer_with_spock/pom.xml [以下示例]展示了如何做到这一点。

如果您支持以 `Spec`结尾的测试类命名约定,您还需要调整您的 Maven Surefire 插件设置,如 {samples_url}/producer_with_spock/pom.xml[以下示例]所示。