Contract DSL
Spring Cloud Contract 支持以以下语言编写的 DSL:
-
Groovy
-
YAML
-
Java
-
Kotlin
Spring Cloud Contract 支持在单个文件中定义多个合同(在 Groovy 中,返回列表而不是单个合同)。 |
以下示例显示契约定义:
Unresolved directive in project-features-contract.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy[]
Unresolved directive in project-features-contract.adoc - include::{verifier_root_path}/src/test/resources/yml/contract_rest.yml[]
Unresolved directive in project-features-contract.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_rest.java[]
Unresolved directive in project-features-contract.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_rest.kts[]
可以使用以下 standalone Maven 命令将契约编译为存根映射: mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert |
Contract DSL in Groovy
Contract DSL in Groovy
如果你不熟悉 Groovy,请不要担心。也可以在 Groovy DSL 文件中使用 Java 语法。
如果你决定用 Groovy 编写契约,即使你以前从未使用过 Groovy,也不必感到惊慌。实际上并不需要了解该语言,因为 Contract DSL 只使用它的一小部分(仅限于文字、方法调用和闭包)。此外,DSL 是静态类型的,即使不了解 DSL 本身,也可以让程序员阅读。
请记住,在 Groovy 合约文件中,你必须为 Contract
类和 make
静态导入(例如 org.springframework.cloud.spec.Contract.make { … }
)提供限定名称。你还可以为 Contract
类 (import org.springframework.cloud.spec.Contract
) 提供导入,然后调用 Contract.make { … }
。
Contract DSL in Java
Contract DSL in Java
要在 Java 中编写契约定义,你需要创建一个类来实现 Supplier<Contract>
接口(用于单个契约)หรือ Supplier<Collection<Contract>>
(用于多个契约)。
你也可以在 src/test/java
(例如 src/test/java/contracts
)下编写契约定义,这样就无需修改项目中的类路径。在这种情况下,你必须向 Spring Cloud Contract 插件提供契约定义的新位置。
以下示例(在 Maven 和 Gradle 中)将在 src/test/java
下提供契约定义:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<contractsDirectory>src/test/java/contracts</contractsDirectory>
</configuration>
</plugin>
contracts {
contractsDslDir = new File(project.rootDir, "src/test/java/contracts")
}
Contract DSL in Kotlin
Contract DSL in Kotlin
要开始用 Kotlin 编写合同,你需要从(新创建的)Kotlin Script 文件(.kts
)开始。与 Java DSL 一样,你可以将你的合同放在你选择的任何目录中。默认情况下,Maven 插件将查看 src/test/resources/contracts
目录,Gradle 插件将查看 src/contractTest/resources/contracts
目录。
自 3.0.0 版本起,Gradle 插件也会查看 |
你需要明确地将 spring-cloud-contract-spec-kotlin
依赖项传递给你的项目插件设置。以下示例(在 Maven 和 Gradle 中)展示了如何执行此操作:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<!-- some config -->
</configuration>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-spec-kotlin</artifactId>
<version>${spring-cloud-contract.version}</version>
</dependency>
</dependencies>
</plugin>
<dependencies>
<!-- Remember to add this for the DSL support in the IDE and on the consumer side -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-spec-kotlin</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
buildscript {
repositories {
// ...
}
dependencies {
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$\{scContractVersion}"
}
}
dependencies {
// ...
// Remember to add this for the DSL support in the IDE and on the consumer side
testImplementation "org.springframework.cloud:spring-cloud-contract-spec-kotlin"
// Kotlin versions are very particular down to the patch version. The <kotlin_version> needs to be the same as you have imported for your project.
testImplementation "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:<kotlin_version>"
}
请记住,在 Kotlin 脚本文件中,你必须为 ContractDSL
类提供限定名称。你通常会像这样使用它的 contract 函数:org.springframework.cloud.contract.spec.ContractDsl.contract { … }
。你还可以为 contract
函数 (import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract
) 提供导入,然后调用 contract { … }
。
Contract DSL in YAML
Contract DSL in YAML
要查看 YAML 合同的架构,请访问 YML Schema 页面。
Limitations
Limitations
验证 JSON 数组大小的支持为实验性功能。如果你想启用它,请将以下系统属性的值设为 true
:spring.cloud.contract.verifier.assert.size
。此功能的默认设置是 false
。你还可以设置插件配置中的 assertJsonSize
属性。
由于 JSON 结构可能有任意形式,使用 Groovy DSL 和 GString
中的 value(consumer(…), producer(…))
符号时可能无法正确解析它。因此,你应该使用 Groovy Map 符号。
Multiple Contracts in One File
Multiple Contracts in One File
你可以在一个文件中定义多个合同。此类合同可能类似于以下示例:
Unresolved directive in _dsl-multiple.adoc - include::{plugins_path}/spring-cloud-contract-maven-plugin/src/test/projects/multiple-contracts/src/test/resources/contracts/com/hello/v1/WithList.groovy[]
Unresolved directive in _dsl-multiple.adoc - include::{verifier_root_path}/src/test/resources/yml/multiple_contracts.yml[]
class contract implements Supplier<Collection<Contract>> {
@Override
public Collection<Contract> get() {
return Arrays.asList(
Contract.make(c -> {
c.name("should post a user");
// ...
}), Contract.make(c -> {
// ...
}), Contract.make(c -> {
// ...
})
);
}
}
import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract
arrayOf(
contract {
name("should post a user")
// ...
},
contract {
// ...
},
contract {
// ...
}
}
在前面的示例中,一个合同具有 name
字段,另一个没有。这会导致生成两个看起来像以下的测试:
package org.springframework.cloud.contract.verifier.tests.com.hello;
import com.example.TestBase;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
import com.jayway.restassured.response.ResponseOptions;
import org.junit.Test;
import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
import static org.assertj.core.api.Assertions.assertThat;
public class V1Test extends TestBase {
@Test
public void validate_should_post_a_user() throws Exception {
// given:
MockMvcRequestSpecification request = given();
// when:
ResponseOptions response = given().spec(request)
.post("/users/1");
// then:
assertThat(response.statusCode()).isEqualTo(200);
}
@Test
public void validate_withList_1() throws Exception {
// given:
MockMvcRequestSpecification request = given();
// when:
ResponseOptions response = given().spec(request)
.post("/users/2");
// then:
assertThat(response.statusCode()).isEqualTo(200);
}
}
请注意,对于具有 name
字段的合同,生成的测试方法被命名为 validate_should_post_a_user
。未具有 name
字段的那个被称为 validate_withList_1
。它对应于文件 WithList.groovy
的名称和列表中合同的索引。
生成的存根显示在以下示例中:
should post a user.json
1_WithList.json
第一个文件从契约获取了 name
参数。第二个文件使用索引(本例中,契约在文件契约列表中的索引为 1
)作为前缀获取契约文件名(WithList.groovy
)。
命名合同更好,因为这样做让你的测试更具意义。 |
Stateful Contracts
Stateful Contracts
有状态合约(也称为场景)是应按顺序读取的合约定义。这在以下情况下可能有用:
-
因为你使用 Spring Cloud Contract 测试你的有状态应用,所以你想要按一个明确定义的顺序调用合约。
我们非常不建议你这样做,因为合约测试应该是无状态的。 |
-
你想要同一端点对于同一个请求返回不同的结果。
要创建有状态合约(或场景),需要在创建合约时使用适当的命名约定。约定要求包含一个后跟下划线的顺序号。无论使用 YAML 还是 Groovy,这都适用。以下列表显示了一个示例:
my_contracts_dir\
scenario1\
1_login.groovy
2_showCart.groovy
3_logout.groovy
这样的树会导致 Spring Cloud Contract Verifier 生成名称为“scenario1”且存在以下三步的 WireMock 场景:
-
login
,标记为Started
指向… -
showCart
,标记为Step1
指向… -
logout
,标记为Step2
(该标记关闭场景)。
您可以在 https://wiremock.org/docs/stateful-behaviour/ 找到有关 WireMock 场景的更多详细信息。