Gradle Project

Prerequisites

若要将 Spring Cloud Contract 验证程序与 WireMock 配合使用,你必须使用 Gradle 插件或 Maven 插件。

如果你想在项目中使用 Spock,则必须分别添加 spock-corespock-spring 模块。有关详细信息,请参阅 Spock’s documentation

Add Gradle Plugin with Dependencies

若要添加带有依赖项的 Gradle 插件,你可以使用类似于以下内容的代码:

  • Plugin DSL GA versions

  • Plugin DSL non GA versions

  • Legacy Plugin Application

// 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 and 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 在类路径中,并相应地修改导入。

Snapshot Versions for Gradle

你可以将其他快照存储库添加到你的 settings.gradle 以使用快照版本,这些版本会在每次成功构建之后自动上传,如下所示:

link:{standalone_samples_path}/http-server/settings.gradle[role=include]
}

Add stubs

默认情况下,Spring Cloud Contract 验证程序会在 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 验证程序假定其中至少包含一个要作为测试类名称使用的目录级别。如果存在多个级别的嵌套目录,则最后一个除外的所有目录都被用作包名称。考虑以下结构:

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

根据前面的结构,Spring Cloud Contract 验证程序创建一个名为 defaultBasePackage.MyService 的测试类,其中包含两个方法:

  • shouldCreateUser()

  • shouldReturnUser()

Running the Plugin

该插件注册自己以在 check 任务之前被调用。如果你希望它成为构建过程的一部分,则你不必执行任何其他操作。如果你只想生成测试,请调用 generateContractTests 任务。

Default Setup

默认 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

Configuring the Plugin

若要更改默认配置,你可以将 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 原 生地提供的自定义现有任务机制:

为了这个例子,我们希望向 verifierStubsJar 添加 git.properties 文件。

verifierStubsJar {
    from("$\{buildDir}/resources/main/") {
        include("git.properties")
    }
}

此外,需要注意的是,从 3.0.0 开始,默认发布已禁用。结果是,这意味着您可以创建任何命名的 jar 并照常发布,就像通过 Gradle 配置选项所做的那样。这意味着您可以构建完全自定义的 jar 文件,并将其发布,以便对 jar 的布局和内容拥有完全控制。

Configuration Options

  • testMode:定义验收测试的模式。默认情况下,该模式为 MockMvc,它基于 Spring 的 MockMvc。它还可以更改为 WebTestClient、JaxRsClient 或 Explicit(用于实际的 HTTP 调用)。

  • imports: 创建一个数组,其中包含应包含在生成的测试中(例如 ['org.myorg.Matchers'])的导入。默认情况下,它创建一个空数组。

  • staticImports: 创建一个数组,其中包含应包含在生成的测试中的静态导入(例如 ['org.myorg.Matchers.*'])。默认情况下,它创建一个空数组。

  • basePackageForTests: 指定所有生成测试的基础包。如果没有设置,此值将从 baseClassForTests 的包和 packageWithBaseClasses 的包中选取。如果这两个值都没有设置,则会将此值设置为 org.springframework.cloud.contract.verifier.tests

  • baseClassForTests: 创建所有生成测试的基础类。默认情况下,如果您使用 Spock 类,这个类为 spock.lang.Specification

  • packageWithBaseClasses:定义所有基类所在的一个包。此设置优先于 baseClassForTests

  • baseClassMappings:通过 Antmatcher 显示地将契约包映射到基类的 FQN。此设置优先于 packageWithBaseClassesbaseClassForTests

  • ignoredFiles:使用 Antmatcher 允许定义要跳过处理的存根文件。它默认是空数组。

  • 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 存根的目录。

  • testFramework:指定要使用的目标测试框架。目前支持 Spock、JUnit 4 (TestFramework.JUNIT) 和 JUnit 5,其中 JUnit 4 是默认框架。

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

  • 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 :代理的端口

  • proxyHost :代理的主机

  • cacheDownloadedContracts:如果设置为 true,则会缓存下载非快照合同工件的文件夹。默认为 true

您还可以在该插件中启用以下实验性功能:

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

  • assertJsonSize:您可以在生成的测试中检查 JSON 数组的大小。此功能默认禁用。

Single Base Class for All Tests

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

link:{plugins_path}/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/org/springframework/cloud/contract/verifier/twitter/places/BaseMockMvcSpec.groovy[role=include]

如果您使用“显式”模式,可以使用基类来初始化整个受测应用程序,就像在常规集成测试中看到的那样。如果您使用 JAXRSCLIENT 模式,则此基类还应该包含 protected WebTarget webTarget 字段。目前,测试 JAX-RS API 的唯一选项是启动 Web 服务器。

Different Base Classes for Contracts

如果您的基本类在契约之间有所不同,您可以告诉 Spring Cloud Contract 插件哪个类应该被自动生成的测试扩展。您有两种选择:

  • 通过提供 packageWithBaseClasses 来遵循约定

  • 使用 baseClassMappings 提供显式映射

By Convention

约定如下,如果您在(例如)src/contractTest/resources/contract/foo/bar/baz/ 中有合约,并将 packageWithBaseClasses 属性的值设置为 com.example.base,则 Spring Cloud Contract Verifier 假定 com.example.base 包下有一个 BarBazBase 类。换句话说,该系统采用包的最后两个部分(如果存在),并形成一个带有 Base 后缀的类。此规则优先于 baseClassForTests

By Mapping

您可以手动将合约包的正则表达式映射到匹配合约的基类的完全限定名称。您必须提供一个名为 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

Invoking Generated Tests

为确保提供者一侧符合您定义的合约,您需要运行以下命令:

./gradlew contractTest

Publishing Stubs to Artifact Repository

如果您使用二进制制品存储库来保存存根,则您需要为 Gradle 配置发布部分才能包含 verifierStubsJar。为此,您可以使用以下示例配置:

apply plugin: 'maven-publish'

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

            artifact verifierStubsJar
        }
    }
}

自 3.0.0 以来,内部存根发布已弃用并默认禁用。建议将 verifierStubsJar 与您自己的某个发布一起包含。

Pushing Stubs to SCM

如果您使用 SCM 存储库来保存合约和存根,您可能希望自动化将存根推送到存储库的步骤。为此,您可以通过运行以下命令来调用 pushStubsToScm 任务:

$ ./gradlew pushStubsToScm

Using the SCM Stub Downloader下,您可以找到可以通过`contractsProperties`字段(例如`contracts { contractsProperties = [foo:"bar"] }`)、contractsProperties`方法(例如`contracts { contractsProperties([foo:"bar"]) })或系统属性或环境变量传递的所有可能的配置选项。

Spring Cloud Contract Verifier on the Consumer Side

在使用服务中,您需要以与提供者的情况完全相同的方式配置 Spring Cloud Contract Verifier 插件。如果您不希望使用 Stub Runner,则需要复制存储在 src/contractTest/resources/contracts 中的合约,并使用以下命令生成 WireMock JSON 存根:

./gradlew generateClientStubs

必须设置 stubsOutputDir 选项才能使存根生成正常运行。

如果存在,您可以在自动化测试中使用 JSON 存根来使用服务。以下示例展示了如何执行此操作:

@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 服务。此请求由配置了 Spring Cloud Contract Verifier 生成的存根的 WireMock 服务器处理。