Jonathan Halterman
11/07/2022, 4:38 PMTimothy Jones
11/07/2022, 6:04 PMIs contract testing mainly about catching breaking structural API changes?It depends. I would say contract testing is about confirming whether two systems are able to communicate - in some system designs, this will mainly be about catching breaking structural API changes.
would we want to test several of those, or just one overall test for an update?Test each communication scenario - each time you would be exercising a different meaning or a different code path, add a test for that. Think of it as contract-by-example.
large requests that encompass several smaller components.I don't think I understand this question. Could you elaborate on what you mean by component?
Timothy Jones
11/07/2022, 6:09 PMhow do you determine where to draw the line between functional tests?The reason we don't recommend functional tests driven by pact is because the pact is defined at the consumer side, but you would be testing the functionality of the provider. The consumer doesn't (necessarily) know what the functionality of the provider is. Consider a "create this user" request - the provider will know what the valid username rules are (eg, you're not allowed that character, or this user already exists). The consumer will want to be able to understand several different types of response (say "that user was created", "that username isn't valid" and "that username is taken"). You'd want to have a test for each type of response that you care about, but it's not convenient to have a test for each scenario that might create that response - because if you did you would tightly couple the consumer's tests to the provider behaviour.
Timothy Jones
11/07/2022, 6:09 PMTimothy Jones
11/07/2022, 6:12 PMIs contract testing mainly about catching breaking structural API changes?There's another subtlety that might be worth pointing out there- because Pact is consumer-driven, you're not describing the whole API surface, or even necessarily the whole response payload. You're describing the parts of the API or payload that you're actually using. Removing a part of the payload that no consumer is using wouldn't be a breaking contract change, even if it might be a breaking structural change.
Timothy Jones
11/07/2022, 6:17 PMJonathan Halterman
11/07/2022, 6:49 PMTest each communication scenario - each time you would be exercising a different meaning or a different code path, add a test for that. Think of it as contract-by-example.Hmm, that sounds a bit like functional testing to me. Maybe a bit more about my situation would be helpful. My domain involves managing clustered "deployments" of services. We have one request/resource representing a deployment that you can update in different ways, which somewhat represents different use cases. For example, updating a deployment's version causes it to be upgraded. Upgrading a deployment's node count causes it to be scaled out. These are different use cases, but are accomplished via the same "update deployment" request/API. Would it be appropriate to contract test each of these since the structure of the request is the same, and only the contents of the requests and responses differ?
Jonathan Halterman
11/07/2022, 6:54 PMTimothy Jones
11/07/2022, 6:58 PM{
"status": "deployed"
}
and
{
"status": "updating"
}
then it's useful to explicitly cover those:
"given some service exists, then a request to update that service, responds with the status: updating"Timothy Jones
11/07/2022, 6:59 PM{ "status": "updating" }
to mean that the service is updating", but you're not actually checking whether it isTimothy Jones
11/07/2022, 7:00 PM{ "status": <any string> }
means that you would catch a case where say the provider responds with UPDATING
and the client doesn't understand itJonathan Halterman
11/07/2022, 7:15 PM{
"applicationCount": 3,
"applicationConfig": ...
}
and leads to a response like this:
{
"applications": [
{
"endpoint": ...
"credentials": ...
},
{
"endpoint": ...
"credentials": ...
},
{
"endpoint": ...
"credentials": ...
},
]
}
I imagine it would make sense to assert that some value for applicationCount and applicationConfig leads to the appropriate response, but not necessarily that various different values do. Does that seem right?Timothy Jones
11/07/2022, 7:23 PMTimothy Jones
11/07/2022, 7:23 PMJonathan Halterman
11/07/2022, 7:26 PMJonathan Halterman
11/07/2022, 7:27 PMTimothy Jones
11/07/2022, 7:27 PMTimothy Jones
11/07/2022, 7:27 PMJonathan Halterman
11/07/2022, 7:28 PMTimothy Jones
11/07/2022, 7:28 PMTimothy Jones
11/07/2022, 7:28 PMTimothy Jones
11/07/2022, 7:29 PMTimothy Jones
11/07/2022, 7:30 PMJonathan Halterman
11/07/2022, 7:31 PMDo we want to have tests that cover the entire large object in addition to tests for the smaller components?
I don't think I understand this question. Could you elaborate on what you mean by component?Sure, for example, say we have a "create deployment" request that has a few different sections in it:
{
"databaseConfig": {
"nodeCount": 3,
"nodeSize": "4G",
"partitions": 24
},
"proxyConfig": {
"nodeCount": 3,
"nodeSize": "4G",
"loadBalancing": true
}
}
Would we want to create separate test cases to assert that the databaseConfig
or proxyConfig
are individually valid, or a single test case for the entire request?Jonathan Halterman
11/07/2022, 7:32 PMTimothy Jones
11/07/2022, 7:33 PMTimothy Jones
11/07/2022, 7:34 PMMatt (pactflow.io / pact-js / pact-go)
Jonathan Halterman
11/07/2022, 11:09 PMTimothy Jones
11/08/2022, 12:17 AMJonathan Halterman
11/08/2022, 4:36 AMJeroen van Dijk
11/08/2022, 8:57 AMTimothy Jones
11/08/2022, 6:51 PMShould a consumer test assert that the contents of a response correspond to the contents of a request?I'm not 100% on what you're asking here - I think there are two interpretations, so I'll answer both: If you're asking about what to put in the contract (whether to be specific
"status": "deployed"
, or general with a matcher "status": any string
), then it depends.
If it's part of the expectation of the consumer, then yes. For example, if you say "I'd like to change the status of this object to 'deployed'" and the resulting object is returned in the response, then yes, it should be in the contract. Another yes scenario might be where the consumer relies on it being a specific value (eg, if you're switching display logic on deployed
vs not-deployed
or something).
If you're just displaying it, then no, it doesn't matter. A good guide for "should I use a matcher here" is to realise that using a matcher isn't a description of the response schema, it's a promise that all responses that pass the matcher are covered by the test that you've written.Timothy Jones
11/08/2022, 6:54 PMJSON.parse()
. But, the test is "does the response I am expecting unmarshall into the business object I am expecting", which is worth including in your test for completeness even if that's a 1:1 mapping.Jonathan Halterman
11/08/2022, 6:57 PMTimothy Jones
11/08/2022, 6:59 PMTimothy Jones
11/08/2022, 6:59 PMJonathan Halterman
11/08/2022, 7:00 PMTimothy Jones
11/08/2022, 7:00 PMTimothy Jones
11/08/2022, 7:01 PM