Maven 專案
新增 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 的 classpath 中不可見。為確保它始終存在,你可以將以下條目新增到你的 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 會被新增到 classpath。但是,你可以透過將其新增到外掛的 classpath 來使用 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 存在於 classpath 中,並相應地修改匯入。
使用 Maven 的 Snapshot 和 Milestone 版本
要使用 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>
新增 stub
預設情況下,Spring Cloud Contract Verifier 會在 src/test/resources/contracts
目錄中查詢 stub。包含 stub 定義的目錄被視為類名,每個 stub 定義被視為一個獨立的測試。我們假設它至少包含一個目錄作為測試類名。如果存在多層巢狀目錄,則除最後一層外的所有目錄都用作包名。考慮以下結構
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 執行 stub,請呼叫 run
目標,並將要執行的 stub 指定為 spring.cloud.contract.verifier.stubs
系統屬性,如下所示
mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:run \ -Dspring.cloud.contract.verifier.stubs="com.acme:service-name"
配置外掛
要更改預設配置,你可以將 configuration
部分新增到外掛定義或 execution
定義中,如下所示
<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
: 一個 Map,包含傳遞給 Spring Cloud Contract 元件的屬性。這些屬性可能被(例如)內建或自定義的 Stub Downloaders 使用。 -
failOnNoContracts
: 啟用時,如果未找到任何契約,則丟擲異常。預設為true
。 -
failOnInProgress
: 如果設定為true
,那麼如果發現任何正在進行中的契約,它們將導致構建失敗。在生產者側,你需要明確表明你有正在進行中的契約,並考慮到這可能會在消費者側導致誤報測試結果。預設為true
。 -
incrementalContractTests
: 啟用時,僅當契約自上次構建以來發生更改時才建立測試。預設為true
。 -
incrementalContractStubs
: 啟用時,僅當契約自上次構建以來發生更改時才建立 stub。預設為true
。 -
incrementalContractStubsJar
: 啟用時,僅當 stub 自上次構建以來發生更改時才建立 stub jar。預設為true
。 *httpPort
:WireMock 伺服器的 HTTP 埠,用於提供 stub。目前spring.cloud.contract.verifier.http.port
屬性僅在從目錄提供 stub 時有效。否則,提供 stub id 時,埠必須包含在 id 字串中。 *skip
:設定為true
可跳過驗證器執行。 *skipTestOnly
:設定為true
可跳過驗證器測試生成。 *stubs
:要下載和執行的 stub 列表,使用冒號分隔的 Ivy 符號。 *minPort
:指定 stub 啟動的最小埠。 *maxPort
:指定 stub 啟動的最大埠。 *waitForKeyPressed
:指定啟動 stub 後外掛是否應等待使用者按鍵。 *stubsClassifier
:指定 stub artifact 使用的分類器。
如果你想從 Maven 倉庫下載你的契約定義,可以使用以下選項
-
contractDependency
: 包含所有打包契約的契約依賴項。 -
contractsPath
: 打包契約的 JAR 中具體契約的路徑。預設值為groupid/artifactid
,其中gropuid
是斜槓分隔的。 -
contractsMode
: 選擇查詢和註冊 stub 的模式。 -
deleteStubsAfterTest
: 如果設定為false
,則不從臨時目錄中刪除任何下載的契約。 -
contractsRepositoryUrl
: 包含契約 artifact 的倉庫 URL。如果未提供,則使用當前 Maven 倉庫。 -
contractsRepositoryUsername
: 用於連線到包含契約的倉庫的使用者名稱。 -
contractsRepositoryPassword
: 用於連線到包含契約的倉庫的密碼。 -
contractsRepositoryProxyHost
: 用於連線到包含契約的倉庫的代理主機。 -
contractsRepositoryProxyPort
: 用於連線到包含契約的倉庫的代理埠。
我們僅快取非 snapshot、明確提供的版本(例如 +
或 1.0.0.BUILD-SNAPSHOT
不會被快取)。預設情況下,此功能是開啟的。
以下列表描述了你可以在外掛中開啟的實驗性功能
-
convertToYaml
: 將所有 DSL 轉換為宣告性 YAML 格式。當你使用 Groovy DSL 中的外部庫時,這會非常有用。透過開啟此功能(將其設定為true
),你無需在消費者側新增庫依賴項。 -
assertJsonSize
: 你可以檢查生成的測試中 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 = "https://:" + 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
closure 中工作
<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
。
將 Stub 推送到 SCM
如果你使用 SCM (Source Control Management) 倉庫來儲存契約和 stub,你可能希望自動化將 stub 推送到倉庫的步驟。為此,你可以新增 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 Stub 下載器下,你可以找到所有可能透過 <configuration><contractsProperties>
map、系統屬性或環境變數傳遞的配置選項。例如,你可以指定要 checkout 的具體分支,而不是預設分支
<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 外掛的設定,以下示例展示瞭如何做到這一點。