The need for contract testing originated out of two important evolutions within the software development world:
- The transition of monolithic built software into microservices
- The use of containerization for software
In the microservices architecture, each feature can be created in isolation as a separate service.
For a software product to be built using this architecture it would mean all these separate services have to communicate with each other. This is made possible with APIs.
Now since a microservice can be integrated into several products and has its own independent lifecycle and version, challenges arise as it can’t be tested effectively by only writing integration tests.
‘Consumer-driven’ contract testing, pardon?
Contract testing helps ensure that services can communicate with each other.
This contract is between the client who wants to receive data (consumer) and an API on a server that provides the data the client needs (provider).
Now what happens if the provider updates its service and makes changes?
We will have to validate if the consumer can still communicate with the service and if it will still provide the required response. This validation is made simple through an agreement or contract.
Additionally there can also be multiple consumers of a single provider, and each consumer can expect a different response. As such there is a need for a consumer contract between each consumer and the provider. This contract needs to be verified every time a change is made.
The contract itself is created by the consumer as it has certain expectations from the provider. The provider will in turn verify that its response, confirms the contract. Hence the name consumer contracts and consumer-driven contract testing.
An example
Lets say we have an application with a login service and user service. Both are created/handled by different teams.
For the login service to know the user details, it sends out a GET request with user ID. The user service sends back a response containing user details (firstname, lastname, account type).
Over time the different account types are increased and at one point team B decides to change accountType into accountTypes. The user service is tested and deployed to production.
Team A however, was not informed about this update and will start encountering problems with their login service as it is still expecting the accountType in the user service response.
In reality, chances are these type of bugs are not covered by traditional unit-, integration- or e2e-tests. Contract testing does cover these situations.
Contract testing tools
There are currently several tools available which support contract testing:
- Pact
- Postman
- OpenAPI
- RAML
As Pact specialises purely on contract testing, it is the best overall choice. However, since it is integrated on a unit test level, QA engineers will need to have sufficient coding skills to implement this solution. Documentation and example Pact implementations can be found here.
Alternatively Postman is a firm second choice, based on its ease to set up contract tests, its large community and support. More info can be found here.
Conclusion
Consumer-driven contract testing fills a potentially dangerous gap of test coverage when working with microservices. As more microservices are created and become interdependent, not implementing these type of tests will quickly lead to dependency hell.
Depending on the QA-team maturity the market already provides several quality solutions.
Choose wisely and happy testing!
Written by Tomas Moors, Test Automation Engineer & QA lead.