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 的類路徑中不可見。為確保它始終存在,您可以將以下條目新增到您的 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()
執行外掛
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 元件的屬性的對映。這些屬性可能被(例如)內建或自定義存根下載器使用。 -
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 陣列的大小。此功能預設停用。
所有測試的單一基類
在使用預設 (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 閉包中如何工作
<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 時可能會看到的異常
當您點選錯誤標記時,您應該會看到類似以下內容
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 外掛與 Spock 測試
您可以使用 Spock Framework 建立和執行自動生成的契約驗證測試,包括 Maven 和 Gradle。然而,雖然使用 Gradle 很簡單,但在 Maven 中,您需要一些額外的設定才能使測試正常編譯和執行。
首先,您必須使用一個外掛,例如 GMavenPlus 外掛,將 Groovy 新增到您的專案中。在 GMavenPlus 外掛中,您需要明確設定測試源,包括定義您的基礎測試類的路徑和新增生成的契約測試的路徑。以下示例顯示瞭如何操作。
如果您遵循 Spock 將測試類名以 Spec 結尾的約定,您還需要調整您的 Maven Surefire 外掛設定,如 以下示例 所示。