|
此版本仍在开发中,目前尚不被视为稳定版本。如需最新稳定版本,请使用 spring-cloud-contract 5.0.2! |
Maven Project
添加 Maven 插件
要添加 Spring Cloud Contract BOM,请在您的 pom.xml 文件中包含以下部分:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>${spring-cloud-contract.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
接下来,添加 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>
有时,无论选择哪种 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 和 Rest Assured 2.0
默认情况下,Rest Assured 3.x 被添加到类路径中。然而,您可以通过将 Rest Assured 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 已存在于类路径中,并据此相应地修改导入语句。
使用快照和里程碑版本进行 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>
</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>
</pluginRepositories>
添加存根
默认情况下,Spring Cloud Contract Verifier 在 src/test/resources/contracts 目录中查找存根(stubs)。包含存根定义的目录被视为一个类名,而每个存根定义则被视为一个独立的测试用例。我们假设该目录至少包含一个子目录,用于作为测试类的名称。如果存在多层嵌套目录,则除最后一层外的所有目录均用作包名。考虑以下结构:
src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy
鉴于该结构,Spring Cloud Contract Verifier 会创建一个名为 defaultBasePackage.MyService 的测试类,并包含两个方法:
-
shouldCreateUser() -
shouldReturnUser()
运行插件
插件 generateTests 的目标被分配为在称为 generate-test-sources 的构建阶段中调用。如果您希望它成为构建流程的一部分,无需进行任何操作。如果您仅想生成测试,请调用 generateTests 目标。
如果您希望从 Maven 运行存根,请调用 run 目标,并将要运行的存根作为 spring.cloud.contract.verifier.stubs 系统属性传入,如下所示:
mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:run \-Dspring.cloud.contract.verifier.stubs="com.acme:service-name"
配置插件
要更改默认配置,您可以在插件定义或 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>
配置选项
-
testMode:定义测试模式。默认模式为MockMvc,基于Spring的MockMvc。你也可以将其改为WebTestClient、JaxRsClient或Explicit(进行真实的HTTP调用)。 -
basePackageForTests: 指定所有生成测试的基本包。如果未设置, 值从baseClassForTests和packageWithBaseClasses中选择。如果这两个值均未设置,则值为org.springframework.cloud.contract.verifier.tests。 -
ruleClassForTests: 指定应添加到生成的测试类中的规则。 -
baseClassForTests: 创建所有生成测试的基础类。默认情况下,如果您 使用 Spock 类,则该类是spock.lang.Specification。 -
contractsDirectory: 指定包含使用 Groovyn 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/并映射.* → com.example.base.BaseClass属性,那么从这些契约生成的测试类扩展自com.example.base.BaseClass。此设置优先于packageWithBaseClasses和baseClassForTests。 -
contractsProperties: 一个包含要传递给 Spring Cloud Contract 组件的属性的映射。这些属性可能被(例如)内置或自定义存档下载器所使用。 -
failOnNoContracts: 当启用时,如果没有找到合同将抛出异常。默认值为true。 -
failOnInProgress:如果设置为true,那么,如果有发现正在进行的合约,就会中断构建。在生产者端,您需要明确表示您有进行中的合约,并考虑到您可能会在消费者端引起测试结果的误报。默认值为true。 -
incrementalContractTests: 在启用时,只有在上次生成后契约发生更改时才创建测试。默认值为true。 -
incrementalContractStubs: 当启用时,只有在上一次构建后合约已更改的情况下才会创建存档。默认为true。 -
incrementalContractStubsJar: 启用时,仅在存档自上次生成以来已更改时创建存档存档。默认值为true。httpPort:提供存档的 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: 选择 stubs 的查找和注册模式。 -
deleteStubsAfterTest: 如果设置为false,则不删除临时目录中下载的任何合约。 -
contractsRepositoryUrl: 在具有合约的工件的存储库的URL。如果未提供,请使用当前的Maven。 -
contractsRepositoryUsername: 用于连接具有合同的仓库的用户名。 -
contractsRepositoryPassword: 连接到带合同的存储库时要使用的密码。 -
contractsRepositoryProxyHost: 代理主机用于连接到具有合同的 repo。 -
contractsRepositoryProxyPort: 用于连接具有合同的仓库的代理端口。
我们仅缓存非快照版本,明确提供的版本(例如+或1.0.0.BUILD-SNAPSHOT不会被缓存)。默认情况下,此功能处于打开状态。
下表描述了可以在插件中打开的可实验性功能列表:
-
convertToYaml: 将所有 DSL 转换为声明性 YAML 格式。当您在您的 Groovy DSLs 中使用外部库时,这可能非常有用。通过打开此功能(设置为true),用户无需在使用者端添加库依赖项。 -
0:可以检查生成测试中JSON数组的大小。此功能默认处于禁用状态。
继承所有测试的基类
在默认情况下(MockMvc)使用 Spring Cloud Contract Verifier 时,需要为所有生成的验收测试创建一个基础规范。在此类中,需要指向要验证的端点。以下示例展示了如何进行操作:
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.web.context.WebApplicationContext;
@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模式,这个基类还应该包含protected WebTarget webTarget字段。现在,测试JAX-RS API的唯一方法是启动Web服务器。
使用不同基类的契约(Contracts)
如果基类在合同之间有所不同,您可以告诉Spring Cloud Contract插件哪个类应该由自动生成的测试来扩展。您有两个选项:
-
遵循约定,为
packageWithBaseClasses提供值 -
提供显式映射,使用
baseClassMappings
通过约定
约定是:如果你有一个在(例如)
src/test/resources/contract/foo/bar/baz/ 的契约,并将
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>
通过映射
您可以将契约包的正则表达式手动映射到匹配契约的基本类的完全限定名。您必须提供一个名为baseClassMappings的列表,该列表由包含baseClassMapping个对象的集合组成,每个对象都采用contractPackageRegex到baseClassFQN个映射。考虑下面的示例:
<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>
假设您在这两个位置下有合同:
-
src/test/resources/contract/com/ -
src/test/resources/contract/foo/
通过提供baseClassForTests,如果映射失败,我们可以有一个回退。
(您还可以提供packageWithBaseClasses作为回退。这样,从src/test/resources/contract/com/契约生成的测试扩展了com.example.ComBase,而其余的测试扩展自com.example.FooBase)。
调用生成的测试
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 test。
将Mock对象推送到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>
在使用 SCM 存档下载器下,您可以找到所有可能通过<configuration><contractsProperties>映射、系统属性或环境变量传递的配置选项。例如,您可以指定要检出的具体分支,而不是默认分支。
<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 插件和 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>
基于 Spock 测试的 Maven 插件
您可以选择使用 Spock Framework 来创建和运行自动生成的合约验证测试,无论是使用Maven还是Gradle。但是,虽然使用Gradle很简单,但在Maven中,为了使测试能够正确编译和执行,您需要进行一些额外的设置。
首先,您必须使用插件,例如 GMavenPlus 插件, 才能将 Groovy 添加到您的项目中。在 GMavenPlus 插件中,您需要显式设置测试源,包括基类测试类定义的位置以及生成的合同测试添加的位置。 下面的示例展示了如何操作。
如果遵守Spock约定,以Spec结尾测试类名,还需要调整Maven Surefire插件设置,如下面的例子所示。