Gradle 專案

先決條件

若要搭配 WireMock 使用 Spring Cloud Contract Verifier,您必須使用 Gradle 或 Maven 外掛程式。

如果您想在專案中使用 Spock,您必須另外新增 spock-corespock-spring 模組。請參閱 Spock 文件以取得更多資訊。

新增具有依賴項的 Gradle 外掛程式

若要新增具有依賴項的 Gradle 外掛程式,您可以使用類似以下的程式碼

  • 外掛程式 DSL GA 版本

  • 外掛程式 DSL 非 GA 版本

  • 舊版外掛程式應用

// build.gradle
plugins {
  id "groovy"
  // this will work only for GA versions of Spring Cloud Contract
  id "org.springframework.cloud.contract" version "$\{GAVerifierVersion}"
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$\{GAVerifierVersion}"
	}
}

dependencies {
	testImplementation "org.apache.groovy:groovy-all:$\{groovyVersion}"
	// example with adding Spock core and Spock Spring
	testImplementation "org.spockframework:spock-core:$\{spockVersion}"
	testImplementation "org.spockframework:spock-spring:$\{spockVersion}"
	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
// settings.gradle
pluginManagement {
	plugins {
		id "org.springframework.cloud.contract" version "$\{verifierVersion}"
	}
    repositories {
        // to pick from local .m2
        mavenLocal()
        // for snapshots
        maven { url "https://repo.spring.io/snapshot" }
        // for milestones
        maven { url "https://repo.spring.io/milestone" }
        // for GA versions
        gradlePluginPortal()
    }
}

// build.gradle
plugins {
  id "groovy"
  id "org.springframework.cloud.contract"
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$\{verifierVersion}"
	}
}

dependencies {
	testImplementation "org.apache.groovy:groovy-all:$\{groovyVersion}"
	// example with adding Spock core and Spock Spring
	testImplementation "org.spockframework:spock-core:$\{spockVersion}"
	testImplementation "org.spockframework:spock-spring:$\{spockVersion}"
	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
// build.gradle
buildscript {
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath "org.springframework.boot:spring-boot-gradle-plugin:$\{springboot_version}"
		classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$\{verifier_version}"
        // here you can also pass additional dependencies such as Kotlin spec e.g.:
        // classpath "org.springframework.cloud:spring-cloud-contract-spec-kotlin:$\{verifier_version}"
	}
}

apply plugin: 'groovy'
apply plugin: 'org.springframework.cloud.contract'

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$\{verifier_version}"
	}
}

dependencies {
	testImplementation "org.apache.groovy:groovy-all:$\{groovyVersion}"
	// example with adding Spock core and Spock Spring
	testImplementation "org.spockframework:spock-core:$\{spockVersion}"
	testImplementation "org.spockframework:spock-spring:$\{spockVersion}"
	testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}

Gradle 和 Rest Assured 2.0

預設情況下,Rest Assured 3.x 會新增至類別路徑。但是,若要使用 Rest Assured 2.x,您可以改為新增它,如下列清單所示

buildscript {
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath "org.springframework.boot:spring-boot-gradle-plugin:$\{springboot_version}"
		classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$\{verifier_version}"
	}
}

dependencies {
    // all dependencies
    // you can exclude rest-assured from spring-cloud-contract-verifier
    testCompile "com.jayway.restassured:rest-assured:2.5.0"
    testCompile "com.jayway.restassured:spring-mock-mvc:2.5.0"
}

這樣一來,外掛程式會自動偵測到 Rest Assured 2.x 存在於類別路徑中,並據此修改匯入。

Gradle 的快照版本

您可以將額外的快照儲存庫新增至您的 `settings.gradle` 以使用快照版本,這些版本會在每次成功建置後自動上傳,如下列清單所示

pluginManagement {
    repositories {
        mavenLocal()
        maven { url "https://repo.spring.io/snapshot" }
        maven { url "https://repo.spring.io/milestone" }
        gradlePluginPortal()
    }
}

新增 Stub

預設情況下,Spring Cloud Contract Verifier 會在 `src/contractTest/resources/contracts` 目錄中尋找 Stub。為了過渡目的,外掛程式也會在 `src/test/resources/contracts` 中尋找合約,但是,自 Spring Cloud Contract 3.0.0 起,此目錄已棄用。

還應注意,使用這個新的 Gradle 來源集,您也應該將合約測試中使用的任何基底類別遷移到 `src/contractTest/{language}`,其中 `{language}` 應根據您的用途替換為 Java 或 Groovy。

包含 Stub 定義的目錄會被視為類別名稱,而每個 Stub 定義都會被視為單一測試。Spring Cloud Contract Verifier 假設它至少包含一層目錄,這些目錄將用作測試類別名稱。如果存在多層巢狀目錄,則除了最後一層之外的所有目錄都將用作套件名稱。請考慮以下結構

src/contractTest/resources/contracts/myservice/shouldCreateUser.groovy
src/contractTest/resources/contracts/myservice/shouldReturnUser.groovy

給定上述結構,Spring Cloud Contract Verifier 會建立一個名為 `defaultBasePackage.MyService` 的測試類別,其中包含兩個方法

  • shouldCreateUser()

  • shouldReturnUser()

執行外掛程式

外掛程式會註冊自身,以便在 `check` 工作之前調用。如果您希望它成為建置過程的一部分,則無需執行任何其他操作。如果您只想產生測試,請調用 `generateContractTests` 工作。

預設設定

預設的 Gradle 外掛程式設定會建立以下建置的 Gradle 部分(以虛擬碼表示)

contracts {
    testFramework ='JUNIT'
    testMode = 'MockMvc'
    generatedTestJavaSourcesDir = project.file("$\{project.buildDir}/generated-test-sources/contractTest/java")
    generatedTestGroovySourcesDir = project.file("$\{project.buildDir}/generated-test-sources/contractTest/groovy")
    generatedTestResourcesDir = project.file("$\{project.buildDir}/generated-test-resources/contracts")
    contractsDslDir = project.file("$\{project.projectDir}/src/contractTest/resources/contracts")
    basePackageForTests = 'org.springframework.cloud.verifier.tests'
    stubsOutputDir = project.file("$\{project.buildDir}/stubs")
    sourceSet = null
}

def verifierStubsJar = tasks.register(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateClientStubs') {
    baseName = project.name
    classifier = contracts.stubsSuffix
    from contractVerifier.stubsOutputDir
}

def copyContracts = tasks.register(type: Copy, name: 'copyContracts') {
    from contracts.contractsDslDir
    into contracts.stubsOutputDir
}

verifierStubsJar.dependsOn copyContracts

設定外掛程式

若要變更預設設定,您可以將 `contracts` 代码片段新增至您的 Gradle 設定,如下列清單所示

contracts {
	testMode = 'MockMvc'
	baseClassForTests = 'org.mycompany.tests'
	generatedTestJavaSourcesDir = project.file('src/generatedContract')
}

若要從遠端來源下載合約,您可以根據需要使用下列程式碼片段

contracts {
    // If your contracts exist in a JAR archive published to a Maven repository
    contractDependency {
        stringNotation = ''
        // OR
        groupId = ''
        artifactId = ''
        version = ''
        classifier = ''
    }

    // If your contracts exist in a Git SCM repository
    contractRepository {
        repositoryUrl = ''
        // username = ''
        // password = ''
    }

    // controls the nested location to find the contracts in either the JAR or Git SCM source
    contractsPath = ''
}

由於我們使用的是 Gradle 的 Jar 封裝工作,因此您可以使用多種選項和功能來進一步擴展 `verifierStubsJar` 建立的內容。為了做到這一點,您可以使用 Gradle 直接提供的原生機制來自訂現有的工作,如下所示

為了舉例說明,我們希望將 `git.properties` 檔案新增至 `verifierStubsJar`。
verifierStubsJar {
    from("$\{buildDir}/resources/main/") {
        include("git.properties")
    }
}

還應注意,自 3.0.0 起,預設發佈已停用。因此,這表示您可以建立任何命名的 jar 並像平常一樣透過 Gradle 設定選項發佈它。這表示您可以建置一個完全按照您想要的方式自訂的 jar 檔案,並發佈它以完全控制 jar 的版面配置和內容。

組態選項

  • testMode:定義驗收測試的模式。預設情況下,模式為 MockMvc,它基於 Spring 的 MockMvc。它也可以變更為 WebTestClient、JaxRsClient 或 Explicit(用於真實 HTTP 呼叫)。

  • imports:建立一個包含應包含在產生測試中的匯入的陣列(例如,['org.myorg.Matchers'])。預設情況下,它會建立一個空陣列。

  • staticImports:建立一個包含應包含在產生測試中的靜態匯入的陣列(例如,['org.myorg.Matchers.*'])。預設情況下,它會建立一個空陣列。

  • basePackageForTests:指定所有產生測試的基底套件。如果未設定,則從 baseClassForTestspackageWithBaseClasses 的套件中選取值。如果未設定這些值,則值會設定為 org.springframework.cloud.contract.verifier.tests

  • baseClassForTests:為所有產生測試建立基底類別。預設情況下,如果您使用 Spock 類別,則類別為 spock.lang.Specification

  • packageWithBaseClasses:定義所有基底類別所在的套件。此設定優先於 baseClassForTests

  • baseClassMappings:明確地將合約套件對應到基底類別的 FQN。此設定優先於 packageWithBaseClassesbaseClassForTests

  • ignoredFiles:使用 Antmatcher 以允許定義應跳過處理的 Stub 檔案。預設情況下,它是一個空陣列。

  • contractsDslDir:指定包含使用 GroovyDSL 撰寫的合約的目錄。預設情況下,其值為 $projectDir/src/contractTest/resources/contracts

  • generatedTestSourcesDir:指定應放置從 Groovy DSL 產生的測試的測試來源目錄。(已棄用)

  • generatedTestJavaSourcesDir:指定應放置從 Groovy DSL 產生的 Java/JUnit 測試的測試來源目錄。預設情況下,其值為 $buildDir/generated-tes-sources/contractTest/java

  • generatedTestGroovySourcesDir:指定應放置從 Groovy DSL 產生的 Groovy/Spock 測試的測試來源目錄。預設情況下,其值為 $buildDir/generated-test-sources/contractTest/groovy

  • generatedTestResourcesDir:指定應放置從 Groovy DSL 產生的測試所使用資源的測試資源目錄。預設情況下,其值為 $buildDir/generated-test-resources/contractTest

  • stubsOutputDir:指定應放置從 Groovy DSL 產生的 WireMock Stub 的目錄。

  • testFramework:指定要使用的目標測試框架。目前,支援 Spock、JUnit 4 (`TestFramework.JUNIT`) 和 JUnit 5,其中 JUnit 4 是預設框架。

  • contractsProperties:一個包含要傳遞給 Spring Cloud Contract 组件的屬性的 Map。這些屬性可能會被(例如)內建或自訂 Stub 下載器使用。

  • sourceSet:儲存合約的來源集。如果未提供,則會假設為 `contractTest`(例如,JUnit 的 `project.sourceSets.contractTest.java` 或 Spock 的 `project.sourceSets.contractTest.groovy`)。

當您想要指定包含合約的 JAR 的位置時,可以使用以下屬性

  • contractDependency:指定提供 `groupid:artifactid:version:classifier` 座標的依賴項。您可以使用 `contractDependency` 閉包來設定它。

  • contractsPath:指定 jar 的路徑。如果已下載合約依賴項,則路徑預設為 `groupid/artifactid`,其中 `groupid` 是以斜線分隔。否則,它會掃描提供的目錄下的合約。

  • contractsMode:指定下載合約的模式(JAR 是否可離線、遠端等等)。

  • deleteStubsAfterTest:如果設定為 `false`,則不會從暫存目錄中移除任何下載的合約。

  • failOnNoContracts:啟用後,如果找不到任何合約,將會擲回例外狀況。預設為 `true`。

  • failOnInProgress:如果設定為 `true`,則如果找到任何正在進行中的合約,它們將會中斷建置。在生產者端,您需要明確說明您有正在進行中的合約,並考慮到您可能會在消費者端造成誤報的測試結果。預設為 `true`。

還有 contractRepository { …​ } 閉包,其中包含以下屬性

  • repositoryUrl:具有合約定義的儲存庫的 URL

  • username:儲存庫使用者名稱

  • password:儲存庫密碼

  • proxyPort:Proxy 的 Port

  • proxyHost:Proxy 的 Host

  • cacheDownloadedContracts:如果設定為 `true`,則會快取下載非快照合約成品所在的資料夾。預設為 `true`。

您也可以在外掛程式中開啟以下實驗性功能

  • convertToYaml:將所有 DSL 轉換為宣告式 YAML 格式。當您在 Groovy DSL 中使用外部程式庫時,這可能非常有用。透過開啟此功能(透過將其設定為 `true`),您無需在消費者端新增程式庫依賴項。

  • assertJsonSize:您可以檢查產生測試中 JSON 陣列的大小。此功能預設為停用。

所有測試的單一基底類別

當在 MockMvc(預設)中使用 Spring Cloud Contract Verifier 時,您需要為所有產生的驗收測試建立基底規格。在此類別中,您需要指向應驗證的端點。以下範例示範如何執行此操作

abstract class BaseMockMvcSpec extends Specification {

	def setup() {
		RestAssuredMockMvc.standaloneSetup(new PairIdController())
	}

	void isProperCorrelationId(Integer correlationId) {
		assert correlationId == 123456
	}

	void isEmpty(String value) {
		assert value == null
	}

}

如果您使用 Explicit 模式,則可以使用基底類別來初始化整個受測應用程式,就像您在一般整合測試中看到的那樣。如果您使用 JAXRSCLIENT 模式,則此基底類別也應包含 protected WebTarget webTarget 欄位。目前,測試 JAX-RS API 的唯一選項是啟動 Web 伺服器。

合約的不同基底類別

如果您的基底類別在合約之間有所不同,您可以告訴 Spring Cloud Contract 外掛程式,自動產生的測試應擴展哪個類別。您有兩個選項

  • 透過提供 packageWithBaseClasses 來遵循慣例

  • 透過使用 baseClassMappings 提供明確的對應

依慣例

慣例是,如果您在(例如)src/contractTest/resources/contract/foo/bar/baz/ 中有一個合約,並將 packageWithBaseClasses 屬性的值設定為 com.example.base,則 Spring Cloud Contract Verifier 會假設在 com.example.base 套件下有一個 BarBazBase 類別。換句話說,系統會取得套件的最後兩個部分(如果存在),並形成一個帶有 Base 後綴的類別。此規則優先於 baseClassForTests

依對應

您可以手動將合約套件的正規表示式對應到相符合約的基底類別的完整限定名稱。您必須提供一個名為 baseClassMappings 的清單,其中包含 baseClassMapping 物件,這些物件採用 contractPackageRegexbaseClassFQN 的對應。

假設您在以下目錄中有合約

  • src/contractTest/resources/contract/com/

  • src/contractTest/resources/contract/foo/

透過提供 baseClassForTests,我們可以在對應失敗時有一個後備方案。(您也可以提供 packageWithBaseClasses 作為後備方案。)這樣一來,從 src/contractTest/resources/contract/com/ 合約產生的測試會擴展 com.example.ComBase,而其餘測試會擴展 com.example.FooBase

調用產生的測試

為了確保提供者端符合您定義的合約,您需要執行以下命令

./gradlew contractTest

將 Stub 發佈到成品儲存庫

如果您使用二進位成品儲存庫來存放 Stub,則需要設定 Gradle 的發佈區段以包含 `verifierStubsJar`。為此,您可以使用以下範例設定

apply plugin: 'maven-publish'

publishing {
    publications {
        maven(MavenPublication) {
            // other configuration

            artifact verifierStubsJar
        }
    }
}

自 3.0.0 起,內部 Stub 發佈已棄用且預設為停用。建議將 `verifierStubsJar` 與您自己的發佈之一一起包含。

將 Stub 推送到 SCM

如果您使用 SCM 儲存庫來存放合約和 Stub,您可能想要自動執行將 Stub 推送到儲存庫的步驟。為此,您可以透過執行以下命令來調用 `pushStubsToScm` 工作

$ ./gradlew pushStubsToScm

使用 SCM Stub 下載器下,您可以找到所有可能的組態選項,您可以透過 contractsProperties 欄位(例如,contracts { contractsProperties = [foo:"bar"] })、透過 contractsProperties 方法(例如,contracts { contractsProperties([foo:"bar"]) })或透過系統屬性或環境變數來傳遞這些選項。

消費者端的 Spring Cloud Contract Verifier

在消費服務中,您需要以與提供者情況完全相同的方式設定 Spring Cloud Contract Verifier 外掛程式。如果您不想使用 Stub Runner,則需要複製儲存在 `src/contractTest/resources/contracts` 中的合約,並透過使用以下命令產生 WireMock JSON Stub

./gradlew generateClientStubs
必須設定 `stubsOutputDir` 選項才能使 Stub 產生工作。

存在時,您可以在自動化測試中使用 JSON Stub 來消費服務。以下範例示範如何執行此操作

@ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)
class LoanApplicationServiceSpec extends Specification {

 @ClassRule
 @Shared
 WireMockClassRule wireMockRule == new WireMockClassRule()

 @Autowired
 LoanApplicationService sut

 def 'should successfully apply for loan'() {
   given:
 	LoanApplication application =
			new LoanApplication(client: new Client(clientPesel: '12345678901'), amount: 123.123)
   when:
	LoanApplicationResult loanApplication == sut.loanApplication(application)
   then:
	loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED
	loanApplication.rejectionReason == null
 }
}

在前面的範例中,`LoanApplication` 呼叫了 `FraudDetection` 服務。此請求由 WireMock 伺服器處理,該伺服器配置了由 Spring Cloud Contract Verifier 產生的 Stub。