I am working on a small REST service that handles some data and persists it (currently Oracle, working on adding caching).
We are starting to work with consumer driven contracts using the Spring Cloud Contract framework. We are having trouble defining one contract for how we intend to implement the service.
We have a PUT endpoint that takes an input field in the body, which is the primary key in our persistence. In our service, we look that field up in the DB, and if it's there, we want to return the existing data from the DB with a 200 status code, signifying that no new record was added. If that value is not yet in the DB, then we have some logic to generate data for it and insert the data in the DB. We then want to return 201 to signify that data was created.
We have a working contract for the 201 case, because when the generated JUnits run, no data exists and it returns 201. But our consumer also wants a contract for the 200 scenario.
Is there a good way to have the contract execute the same call twice so that we can produce both cases?
Our contract looks like this (stripped down but essentially the same):
Contract.make {
name("givenValidInputs_returnDataAndCreatedStatus")
request {
method 'PUT'
url "/path/to/resource"
headers {
contentType("application/json")
}
body(
"primaryKey": $(p('1234567890123456'), c(regex('^[0-9]+$')))
)
}
response {
status 201
body(
"generatedValue": $(p(regex('^[0-9a-zA-Z]+$')), c('abc123'))
)
}
}
Contract tests are designed to test the technical handshake of an endpoint. It is not supposed to be used to test (stateful) behavior. The difference in http codes between a 200 and a 201 could be considered an edge case but is, in my opnion a semantic one.
So I agree with Marcin Grzejszczak that you should mock the service. With that mocked service you can then mock the behavior if you really insist on having a contract definition for the specific http code.
Other than scenarios, you could specify a specific primary key that will represent that "already exists" situation. The only problem is that when the consumer provides "99999999" when it calls the stub, it would match on both contracts (as this value also matches the provided regex in the 201 contract).
If you simply add "priority: 1" to the contract though, the 200 contract would get precedence over the 201 contract when the specific property is provided.
Contract.make {
name("when put existing, expect 200")
request {
method 'PUT'
url "/path/to/resource"
headers {
contentType("application/json")
}
body(
"primaryKey": "99999999"
)
}
response {
status 200
body(
"generatedValue": $(p(regex('^[0-9a-zA-Z]+$')), c('abc123'))
)
}
priority 1
}
On the producer side, you will have to mock your service to respond correctly when the "99999999" value is supplied.
I dont think I would personally include this http code in contract tests as it represents stateful behavior and is thus semantic and not a technical connection requirement in my opinion. On both sides I would test the behavior of the given situation in a non-integration unit test. Though it is sometimes hard to seperate syntatic from semantic in these kind of situations.