跳到主要内容

Maven 项目

DeepSeek V3 中英对照 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​

要使用快照版和里程碑版,你需要在 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 目录中查找存根。包含存根定义的目录被视为类名,每个存根定义被视为一个单独的测试。我们假设它至少包含一个用作测试类名的目录。如果存在多级嵌套目录,除最后一级外的所有目录都将用作包名。考虑以下结构:

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 运行存根,请调用 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:指定包含使用 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/ 下有一个合约,并映射 .* → com.example.base.BaseClass 属性,则从这些合约生成的测试类将扩展 com.example.base.BaseClass。此设置优先于 packageWithBaseClasses 和 baseClassForTests。

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

  • failOnNoContracts:启用后,当未找到任何合约时将抛出异常。默认为 true。

  • failOnInProgress:如果设置为 true,则当发现任何进行中的合约时,它们会中断构建。在生产者端,你需要明确说明你有进行中的合约,并考虑到你可能在消费者端导致误报的测试结果。默认为 true。

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

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

  • incrementalContractStubsJar:启用后,仅当存根自上次构建以来发生更改时才创建存根 jar。默认为 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,其中 groupid 以斜杠分隔。

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

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

  • contractsRepositoryUrl:包含合约制品的仓库 URL。如果未提供,则使用当前的 Maven 仓库。

  • contractsRepositoryUsername:用于连接到合约仓库的用户名。

  • contractsRepositoryPassword:用于连接到合约仓库的密码。

  • contractsRepositoryProxyHost:用于连接到合约仓库的代理主机。

  • contractsRepositoryProxyPort:用于连接到合约仓库的代理端口。

我们仅缓存非快照、明确指定的版本(例如 + 或 1.0.0.BUILD-SNAPSHOT 不会被缓存)。默认情况下,此功能处于开启状态。

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

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

  • assertJsonSize: 你可以检查生成测试中 JSON 数组的大小。此功能默认处于禁用状态。

所有测试的单一基类​

在使用默认的 Spring Cloud Contract Verifier(MockMvc)时,你需要为所有生成的验收测试创建一个基础规范。在这个类中,你需要指向一个待验证的端点。以下示例展示了如何操作:

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

为合约使用不同的基类​

如果您的合约之间的基类不同,您可以告诉 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。

推送存根到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 时可能遇到的异常情况:

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 插件的配置,如以下示例所示。