我正在尝试使用Spring-Cloud-Contract定义CDC合同,如下所示:
org.springframework.cloud.contract.spec.Contract.make {
request {
method 'GET'
url $(client(~/\/categories\?publication=[a-zA-Z-_]+?/), server('/categories?publication=DMO'))
}
response {
status 200
headers {
header('Content-Type', 'application/json;charset=UTF-8')
}
body """\
[{
"code": "${value(client('DagKrant'), server(~/[a-zA-Z0-9_-]*/))}",
"name": "${value(client('De Morgen Krant'), server(~/[a-zA-Z0-9_\- ]*/))}",
"sections" : []
},
{
"code": "${value(client('WeekendKrant'), server(~/[a-zA-Z0-9_-]*/))}",
"name": "${value(client('De Morgen Weekend'), server(~/[a-zA-Z0-9_\- ]*/))}",
"sections" : [
{
"id" : "${value(client('a984e824'), server(~/[0-9a-f]{8}/))}",
"name" : "${value(client('Binnenland'), server(~/[a-zA-Z0-9_\- ]*/))}"
}
]
}]
"""
}
}
在生成的测试中,这会产生以下断言:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array().contains("code").matches("[a-zA-Z0-9_-]*");
assertThatJson(parsedJson).array().array("sections").contains("id").matches("([0-9a-f]{8})?");
assertThatJson(parsedJson).array().array("sections").contains("name").matches("[a-zA-Z0-9_\\- ]*");
assertThatJson(parsedJson).array().contains("name").matches("[a-zA-Z0-9_\\- ]*");
但在我的测试中,我想允许sections数组为空,就像第一个例子一样。现在,如果我的测试实现返回一个空的sections数组,生成的测试将失败,因为它无法找到空数组的节的id。
Parsed JSON [[{"code":"WeekendKrant","name":"De Morgen Weekend","sections":[]}]]
doesn't match the JSON path [$[*].sections[*][?(@.id =~ /([0-9a-f]{8})?/)]]
我也尝试过使用optional(),但唯一的区别是正则表达式包含'?'在末尾。 JSON断言仍然失败。
在存根中,返回两个结果,但是对于测试,我希望测试也能成功。测试断言是否纯粹是在每个属性的最后一次出现时生成的?是否有可能在阵列上使用'optional()'这样的东西?
答案 0 :(得分:4)
在版本1.0.3.RELEASE之前,不可能像这样进行额外的检查。从该版本开始,您可以提供其他匹配器 - http://cloud.spring.io/spring-cloud-static/spring-cloud-contract/1.0.3.RELEASE/#_dynamic_properties_in_matchers_sections。您可以将byType
与尺寸相关的其他检查进行匹配。
取自文档:
目前,我们仅支持基于JSON Path的匹配器,具有以下匹配可能性。对于stubMatchers:
byEquality() - 通过提供的JSON路径从响应中获取的值需要等于合同中提供的值
byRegex(...) - 通过提供的JSON路径从响应中获取的值需要与正则表达式匹配
byDate() - 通过提供的JSON路径从响应中获取的值需要与ISO Date的正则表达式匹配
byTimestamp() - 通过提供的JSON路径从响应中获取的值需要与ISO DateTime的正则表达式匹配
byTime() - 通过提供的JSON路径从响应中获取的值需要与ISO时间的正则表达式匹配
对于testMatchers:
byEquality() - 通过提供的JSON路径从响应中获取的值需要等于合同中提供的值
byRegex(...) - 通过提供的JSON路径从响应中获取的值需要与正则表达式匹配
byDate() - 通过提供的JSON路径从响应中获取的值需要与ISO Date的正则表达式匹配
byTimestamp() - 通过提供的JSON路径从响应中获取的值需要与ISO DateTime的正则表达式匹配
byTime() - 通过提供的JSON路径从响应中获取的值需要与ISO时间的正则表达式匹配
byType() - 通过提供的JSON路径从响应中获取的值需要与合同中响应正文中定义的类型相同。 byType可以采用闭包,您可以在其中设置minOccurrence和maxOccurrence。这样你就可以断言集合的大小。
例如:
Contract contractDsl = Contract.make {
request {
method 'GET'
urlPath '/get'
body([
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string"
])
stubMatchers {
jsonPath('$.duck', byRegex("[0-9]{3}"))
jsonPath('$.duck', byEquality())
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
jsonPath('$.date', byDate())
jsonPath('$.dateTime', byTimestamp())
jsonPath('$.time', byTime())
}
headers {
contentType(applicationJson())
}
}
response {
status 200
body([
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
valueWithMin: [
1,2,3
],
valueWithMax: [
1,2,3
],
valueWithMinMax: [
1,2,3
],
])
testMatchers {
// asserts the jsonpath value against manual regex
jsonPath('$.duck', byRegex("[0-9]{3}"))
// asserts the jsonpath value against the provided value
jsonPath('$.duck', byEquality())
// asserts the jsonpath value against some default regex
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
// asserts vs inbuilt time related regex
jsonPath('$.date', byDate())
jsonPath('$.dateTime', byTimestamp())
jsonPath('$.time', byTime())
// asserts that the resulting type is the same as in response body
jsonPath('$.valueWithTypeMatch', byType())
jsonPath('$.valueWithMin', byType {
// results in verification of size of array (min 1)
minOccurrence(1)
})
jsonPath('$.valueWithMax', byType {
// results in verification of size of array (max 3)
maxOccurrence(3)
})
jsonPath('$.valueWithMinMax', byType {
// results in verification of size of array (min 1 & max 3)
minOccurrence(1)
maxOccurrence(3)
})
}
headers {
contentType(applicationJson())
}
}
}
生成的测试示例(断言大小的部分)
assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
assertThat(parsedJson.read("$.valueWithMin", java.util.Collection.class).size()).isGreaterThanOrEqualTo(1);
assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
assertThat(parsedJson.read("$.valueWithMax", java.util.Collection.class).size()).isLessThanOrEqualTo(3);
assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
assertThat(parsedJson.read("$.valueWithMinMax", java.util.Collection.class).size()).isStrictlyBetween(1, 3);