Gradle Project

Prerequisites

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

To use Spring Cloud Contract Verifier with WireMock, you must use either a Gradle or a Maven plugin.

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

If you want to use Spock in your projects, you must separately add the spock-core and spock-spring modules. See Spock’s documentation for more information.

Add Gradle Plugin with Dependencies

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

To add a Gradle plugin with dependencies, you can use code similar to the following:

  • 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,你可以像以下清单所示添加它:

By default, Rest Assured 3.x is added to the classpath. However, to use Rest Assured 2.x, you can add it instead, as the following listing shows:

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

That way, the plugin automatically sees that Rest Assured 2.x is present on the classpath and modifies the imports accordingly.

Snapshot Versions for Gradle

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

You can add the additional snapshot repository to your settings.gradle to use snapshot versions, which are automatically uploaded after every successful build, as the following listing shows:

Unresolved directive in gradle-project.adoc - include::{standalone_samples_path}/http-server/settings.gradle[]
}

Add stubs

默认情况下,Spring Cloud Contract 验证程序会在 src/contractTest/resources/contracts 目录中查找 stub。出于过渡目的,该插件也会在 src/test/resources/contracts 中查找契约,但自 Spring Cloud Contract 3.0.0 起,该目录已被弃用。

By default, Spring Cloud Contract Verifier looks for stubs in the src/contractTest/resources/contracts directory. For transitional purposes the plugin will also look for contracts in src/test/resources/contracts, however, this directory is deprecated as of Spring Cloud Contract 3.0.0.

还应注意,使用此新的 Gradle 源集,你还要将契约测试中使用的任何基类迁移到 src/contractTest/{language},其中 {language} 应根据你的需要替换为 Java 或 Groovy。

It should also be noted, that with this new Gradle source set, you should also migrate any base classes used within your contract tests to src/contractTest/{language} where {language} should be replaced with Java or Groovy as needed for your purposes.

包含 stub 定义的目录被视为类名,每个 stub 定义被视为单个测试。Spring Cloud Contract 验证程序假定其中至少包含一个要作为测试类名称使用的目录级别。如果存在多个级别的嵌套目录,则最后一个除外的所有目录都被用作包名称。考虑以下结构:

The directory that contains stub definitions is treated as a class name, and each stub definition is treated as a single test. Spring Cloud Contract Verifier assumes that it contains at least one level of directories that are to be used as the test class name. If more than one level of nested directories is present, all except the last one is used as the package name. Consider the following structure:

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

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

Given the preceding structure, Spring Cloud Contract Verifier creates a test class named defaultBasePackage.MyService with two methods:

  • shouldCreateUser()

  • shouldReturnUser()

Running the Plugin

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

The plugin registers itself to be invoked before a check task. If you want it to be part of your build process, you need do nothing more. If you want only to generate tests, invoke the generateContractTests task.

Default Setup

默认 Gradle 插件设置创建以下 Gradle 部分的构建(伪代码):

The default Gradle Plugin setup creates the following Gradle part of the build (in pseudocode):

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 配置中,如下所示:

To change the default configuration, you can add a contracts snippet to your Gradle configuration, as the following listing shows:

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

若要从远程源下载契约,你可以根据需要使用以下代码片段:

To download contracts from a remote source, you can use the following snippets as needed:

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 原 生地提供的自定义现有任务机制:

Since we are using Gradle’s Jar packaging task, there are several options and capabilities that you may wish to utilize to further extend what is created by the verifierStubsJar. In order to do this, you would use the native mechanisms provided directly by Gradle for customizing an existing task like so:

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

for the sake of the example, we desire to add a git.properties file to the verifierStubsJar.

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

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

It should also be noted that as of 3.0.0, the default publication has been disabled. As a result this means, that you are able to create any named jar and publish it as you would normally have done via Gradle configuration options. This means that you can build a jar file customized just the way you would like and publish that for absolute full control over the jar’s layout and contents.

Configuration Options

  • testMode: Defines the mode for acceptance tests. By default, the mode is MockMvc, which is based on Spring’s MockMvc. It can also be changed to WebTestClient, JaxRsClient, or Explicit (for real HTTP calls).

  • imports: Creates an array with imports that should be included in the generated tests (for example, ['org.myorg.Matchers']). By default, it creates an empty array.

  • staticImports: Creates an array with static imports that should be included in generated tests(for example, ['org.myorg.Matchers.*']). By default, it creates an empty array.

  • basePackageForTests: Specifies the base package for all generated tests. If not set, the value is picked from the package of baseClassForTests and from packageWithBaseClasses. If neither of these values are set, the value is set to org.springframework.cloud.contract.verifier.tests.

  • baseClassForTests: Creates a base class for all generated tests. By default, if you use Spock classes, the class is spock.lang.Specification.

  • packageWithBaseClasses: Defines a package where all the base classes reside. This setting takes precedence over baseClassForTests.

  • baseClassMappings: Explicitly maps a contract package to a FQN of a base class. This setting takes precedence over packageWithBaseClasses and baseClassForTests.

  • ignoredFiles: Uses an Antmatcher to allow defining stub files for which processing should be skipped. By default, it is an empty array.

  • contractsDslDir: Specifies the directory that contains contracts written by using the GroovyDSL. By default, its value is $projectDir/src/contractTest/resources/contracts.

  • generatedTestSourcesDir: Specifies the test source directory where tests generated from the Groovy DSL should be placed. (Deprecrated)

  • generatedTestJavaSourcesDir: Specifies the test source directory where Java/JUnit tests generated from the Groovy DSL should be placed. By default, it’s value is $buildDir/generated-tes-sources/contractTest/java.

  • generatedTestGroovySourcesDir: Specifies the test source directory where Groovy/Spock tests generated from the Groovy DSL should be placed. By default, it’s value is $buildDir/generated-test-sources/contractTest/groovy.

  • generatedTestResourcesDir: Specifies the test resource directory where resources used by the tests generated from the Groovy DSL should be placed. By default, its value is $buildDir/generated-test-resources/contractTest.

  • stubsOutputDir: Specifies the directory where the generated WireMock stubs from the Groovy DSL should be placed.

  • testFramework: Specifies the target test framework to be used. Currently, Spock, JUnit 4 (TestFramework.JUNIT) and JUnit 5 are supported, with JUnit 4 being the default framework.

  • contractsProperties: A map that contains properties to be passed to Spring Cloud Contract components. Those properties might be used by (for example) built-in or custom Stub Downloaders.

  • sourceSet: Source set where the contracts are stored. If not provided will assume contractTest (for example, project.sourceSets.contractTest.java for JUnit or project.sourceSets.contractTest.groovy for Spock).

当您想要指定包含合约的 JAR 的位置时,可以使用以下属性:

You can use the following properties when you want to specify the location of the JAR that contains the contracts:

  • contractDependency: Specifies the Dependency that provides groupid:artifactid:version:classifier coordinates. You can use the contractDependency closure to set it up.

  • contractsPath: Specifies the path to the jar. If contract dependencies are downloaded, the path defaults to groupid/artifactid, where groupid is slash separated. Otherwise, it scans contracts under the provided directory.

  • contractsMode: Specifies the mode for downloading contracts (whether the JAR is available offline, remotely, and so on).

  • deleteStubsAfterTest: If set to false, does not remove any downloaded contracts from temporary directories.

  • failOnNoContracts: When enabled, will throw an exception when no contracts were found. Defaults to true.

  • failOnInProgress: If set to true, then, if any contracts that are in progress are found, they break the build. On the producer side, you need to be explicit about the fact that you have contracts in progress and take into consideration that you might be causing false positive test results on the consumer side. Defaults to true.

还有 contractRepository { …​ } 闭包,它包含以下属性

There is also the contractRepository { …​ } closure that contains the following properties

  • repositoryUrl: The URL to the repository with contract definitions

  • username : The repository username

  • password : The repository password

  • proxyPort : The port of the proxy

  • proxyHost : The host of the proxy

  • cacheDownloadedContracts : If set to true, caches the folder where non-snapshot contract artifacts got downloaded. Defaults to true.

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

You can also turn on the following experimental features in the plugin:

  • convertToYaml: Converts all DSLs to the declarative YAML format. This can be extremely useful when you use external libraries in your Groovy DSLs. By turning this feature on (by setting it to true), you need not add the library dependency on the consumer side.

  • assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default.

Single Base Class for All Tests

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

When using Spring Cloud Contract Verifier in MockMvc (the default), you need to create a base specification for all generated acceptance tests. In this class, you need to point to an endpoint, which should be verified. The following example shows how to do so:

Unresolved directive in gradle-project.adoc - include::{plugins_path}/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/org/springframework/cloud/contract/verifier/twitter/places/BaseMockMvcSpec.groovy[]

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

If you use Explicit mode, you can use a base class to initialize the whole tested application, as you might see in regular integration tests. If you use the JAXRSCLIENT mode, this base class should also contain a protected WebTarget webTarget field. Right now, the only option to test the JAX-RS API is to start a web server.

Different Base Classes for Contracts

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

If your base classes differ between contracts, you can tell the Spring Cloud Contract plugin which class should get extended by the autogenerated tests. You have two options:

  • Follow a convention by providing the packageWithBaseClasses

  • Provide explicit mapping by using baseClassMappings

By Convention

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

The convention is such that, if you have a contract in (for example) src/contractTest/resources/contract/foo/bar/baz/ and set the value of the packageWithBaseClasses property to com.example.base, then Spring Cloud Contract Verifier assumes that there is a BarBazBase class under the com.example.base package. In other words, the system takes the last two parts of the package, if they exist, and forms a class with a Base suffix. This rule takes precedence over baseClassForTests.

By Mapping

您可以手动将合约包的正则表达式映射到匹配合约的基类的完全限定名称。您必须提供一个名为 baseClassMappings 的列表,该列表由 baseClassMapping 对象组成,这些对象采用 contractPackageRegexbaseClassFQN 的映射。

You can manually map a regular expression of the contract’s package to the fully qualified name of the base class for the matched contract. You have to provide a list called baseClassMappings that consists of baseClassMapping objects that take a contractPackageRegex to baseClassFQN mapping.

假设您在以下目录中有合约:

Assume that you have contracts in the following directories:

  • src/contractTest/resources/contract/com/

  • src/contractTest/resources/contract/foo/

通过提供 baseClassForTests,如果映射未成功,我们将有一个后备。(您还可以将 packageWithBaseClasses 作为后备提供。)这样,从 src/contractTest/resources/contract/com/ 合约生成的测试将扩展 com.example.ComBase,而其他测试将扩展 com.example.FooBase

By providing baseClassForTests, we have a fallback in case mapping did not succeed. (You could also provide the packageWithBaseClasses as a fallback.) That way, the tests generated from src/contractTest/resources/contract/com/ contracts extend the com.example.ComBase, whereas the rest of the tests extend com.example.FooBase.

Invoking Generated Tests

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

To ensure that the provider side is compliant with your defined contracts, you need to run the following command:

./gradlew contractTest

Publishing Stubs to Artifact Repository

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

If you use an binary artifact repository to keep the stubs, you will need to configure the publishing section for Gradle to include the verifierStubsJar. To do that, you can use the example configuration below:

apply plugin: 'maven-publish'

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

            artifact verifierStubsJar
        }
    }
}

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

Since 3.0.0, the internal stubs publication has been deprecated and disabled by default. It is recommended to include the verifierStubsJar with one of your own publications.

Pushing Stubs to SCM

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

If you use the SCM repository to keep the contracts and stubs, you might want to automate the step of pushing stubs to the repository. To do that, you can call the pushStubsToScm task by running the following command:

$ ./gradlew pushStubsToScm

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

Under Using the SCM Stub Downloader, you can find all possible configuration options that you can pass either through the contractsProperties field (for example, contracts { contractsProperties = [foo:"bar"] }), through the contractsProperties method (for example, contracts { contractsProperties([foo:"bar"]) }), or through a system property or an environment variable.

Spring Cloud Contract Verifier on the Consumer Side

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

In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin in exactly the same way as in the case of a provider. If you do not want to use Stub Runner, you need to copy the contracts stored in src/contractTest/resources/contracts and generate WireMock JSON stubs by using the following command:

./gradlew generateClientStubs

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

The stubsOutputDir option has to be set for stub generation to work.

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

When present, you can use JSON stubs in automated tests to consume a service. The following example shows how to do so:

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

In the preceding example, LoanApplication makes a call to the FraudDetection service. This request is handled by a WireMock server configured with stubs that were generated by Spring Cloud Contract Verifier.