I wrote a service contract for a REST-endpoint returning a list of bookings like this:
description: test
request:
method: GET
urlPath: /v1/bookings
response:
status: 200
body: '[{"id": 1,"bookingId": 1,"bookingStatus": "ACCEPTED","checkinStatus": "PRE_CHECKIN"}]'
headers:
Content-Type: application/json;charset=UTF-8
matchers:
body:
- path: $..id
type: by_regex
predefined: number
When generating the contract tests for the producer-side (./gradlew generateContractTests), the following code gets generated:
public void validate_test() throws Exception {
// given:
MockMvcRequestSpecification request = given();
// when:
ResponseOptions response = given().spec(request)
.get("/v1/bookings");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).isEqualTo("application/json;charset=UTF-8");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array().isEmpty();
// and:
assertThat((java.lang.Iterable) parsedJson.read("$..id", java.util.Collection.class)).as("$..id").allElementsMatch("-?(\\d*\\.\\d+|\\d+)");
The generated line assertThatJson(parsedJson).array().isEmpty(); must be false, since the service should return something, which is going to be checked afterwards by the matchers.
Same is true for groovy-contract:
body('''[{"id":1,"bookingId":1,"bookingStatus":"CONFIRMED","checkinStatus":"PRE_CHECKIN"}]''')
...is there any other way to return an array in a groovy- or yaml-contract?
Yeah, it's a bug I guess :(
I ran into this same problem after upgrading to 2.0.1.RELEASE. I had to add the following to the bodyMatchers section:
jsonPath('$.[*]', byType { minOccurrence(1) })
Can someone check with latest snapshots if the problem still persists? I can't seem to replicate it in any possible way :|
Seems ok...at least when I look at the generated tests... Thanks!
Awesome! Thanks for checking it out
Seems to be an issue still. Sample to reproduce the issue https://github.com/ThanksForAllTheFish/cloud-contracts
Should be fixed via the latest snapshots. Can someone double check in their code that this is still an issue?
Closing the issue since it should be fixed in the latest snapshots.
Sorry, did not see your message. It seems to still be a problem however:
docker images | grep SNAP
springcloud/spring-cloud-contract 2.1.0.BUILD-SNAPSHOT d5488ef76b00 2 hours ago 693MB
the generated test still contains assertThatJson(parsedJson).field("['content']").field("['three']").isEmpty(); and thus fail. Using the sample project provided above and the step described to reproduce.
@marcingrzejszczak May it be I am misusing the docker image? Can you double check the sample project to see if I am doing something stupid?
I don't even want to comment my support for this issue. I have no idea how I wasn't able to replicate this earlier. Sorry, it was a bug that I've just managed to replicate and fix. You can wait for the new snapshot docker image to pop up on Dockerhub and try again.
Thanks for the help, with 2.1.1.BUILD-SNAPSHOT it is fixed :+1:
Awesome, thanks again for filing the issue, and sorry for my mental blackout ;)
Here we are again. Please have a look at https://github.com/ThanksForAllTheFish/cloud-contracts/tree/still-happening. Let me try to summarize when I see the problem and when not (I can reproduce but it feels weird that I can see it under some circumstances but not in others). Given the actual response:
{
"content":{
"two":"two",
"three":{
"six":"seven"
}
}
}
with the failing contract
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'GET'
url '/list'
}
response {
status 200
body(
[
content: [
three: [
six: "seven"
]
]
]
)
bodyMatchers {
jsonPath('$.content.three.six', byRegex(".*seven.*"))
jsonPath('$.content.two', byRegex(".*two.*"))
}
}
}
generates the dreaded
contracts.ContractVerifierTest > validate_failing FAILED
java.lang.IllegalStateException: Parsed JSON [{"content":{"two":"two","three":{"six":"seven"}}}] with the JSON path [$.['content']] is not empty!
at com.toomuchcoding.jsonassert.JsonAsserter.isEmpty(JsonAsserter.java:202)
at contracts.ContractVerifierTest.validate_failing(ContractVerifierTest.java:35)
However, the succeeding contract
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'GET'
url '/list'
}
response {
status 200
body(
[
content: [
two: "something",
three: [
six: "seven"
]
]
]
)
bodyMatchers {
jsonPath('$.content.three.six', byRegex(".*seven.*"))
jsonPath('$.content.two', byRegex(".*two.*"))
}
}
}
runs successfully.
I also created a new endpoint returning the json
{
"content":{
"three":{
"six":"seven"
}
}
}
and two new contract (named another_) to show more cases. (another_succeeding actually fails, but that is me not knowing how to test for the absence of a specific field, probably a case for byCommand)
It seems to me, when the object in response.body contains only one subfield, like in failing and another_failing we get the wrong generated test, while when there are more than one subfield, the test is generated successfully. This is at least the only rationale I can observe right now.
@marcingrzejszczak I am really sorry, I did not think about testing more permutations.
As a reference, the snapshot version used in the project is springcloud/spring-cloud-contract 2.1.1.BUILD-SNAPSHOT f478d0d7987c 18 hours ago 693MB
Ok now it should work ;) Can you double check the snapshots @ThanksForAllTheFish ?
Yes, even the other cases are covered now (as well as in my real project which is definitely more complex). Hopefully you really got rid of this finally :)