I'm trying to define a CDC contract using Spring-Cloud-Contract like this:
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_\- ]*/))}"
}
]
}]
"""
}
}
In the generated tests, this results in the following assertions:
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_\\- ]*");
But in my tests I want to allow that the sections array is empty, like the first example. Now, if my test implementation returns an empty sections array, the generated tests fail because it cannot find the sections' id for an empty array.
Parsed JSON [[{"code":"WeekendKrant","name":"De Morgen Weekend","sections":[]}]]
doesn't match the JSON path [$[*].sections[*][?(@.id =~ /([0-9a-f]{8})?/)]]
I also tried with optional(), but the only difference is that the regex includes a '?' at the end. The JSON assertion still fails.
In the stubs, both results are returned, but for the test, I want the test to succeed for both, too. Are the test assertions purely generated on the last occurence of each attribute? Is there no possibility to have something like 'optional()' on the array?
It wasn't possible to do additional checks like this up till version 1.0.3.RELEASE. Since that version you can provide additional matchers - http://cloud.spring.io/spring-cloud-static/spring-cloud-contract/1.0.3.RELEASE/#_dynamic_properties_in_matchers_sections . You can match byType
with additional check related to size.
Taken from the docs:
Currently we support only JSON Path based matchers with the following matching possibilities. For stubMatchers:
byEquality() - the value taken from the response via the provided JSON Path needs to be equal to the provided value in the contract
byRegex(…) - the value taken from the response via the provided JSON Path needs to match the regex
byDate() - the value taken from the response via the provided JSON Path needs to match the regex for ISO Date
byTimestamp() - the value taken from the response via the provided JSON Path needs to match the regex for ISO DateTime
byTime() - the value taken from the response via the provided JSON Path needs to match the regex for ISO Time
For testMatchers:
byEquality() - the value taken from the response via the provided JSON Path needs to be equal to the provided value in the contract
byRegex(…) - the value taken from the response via the provided JSON Path needs to match the regex
byDate() - the value taken from the response via the provided JSON Path needs to match the regex for ISO Date
byTimestamp() - the value taken from the response via the provided JSON Path needs to match the regex for ISO DateTime
byTime() - the value taken from the response via the provided JSON Path needs to match the regex for ISO Time
byType() - the value taken from the response via the provided JSON Path needs to be of the same type as the type defined in the body of the response in the contract. byType can take a closure where you can set minOccurrence and maxOccurrence. That way you can assert on the size of the collection.
And example:
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())
}
}
}
and example of a generated test (part for asserting sizes)
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With