Many modern systems are made-up of lots of small components such a microservices and frontends. Each independently built, released and maintained by a bunch of different teams. This is really useful for scaling out your organisation and increasing the speed at which changes can be delivered to your customers.
For people involved in producing this kind of software, it poses a few challenges:
- Will a new version of component X still work with its neighboring components?
- What does component Y even expect of component X? What is the structure of the data X returns? What status codes and headers?
- What version of component X is running in which environment. What version of the interface does X have there?
- How do we start work on X even though we have no clue when Y will be available?
- If I write a mock for Y does it truly simulate its interface or am I blinded by my own assumptions?
- How can I quickly deploy and test X without having to spin up its neighboring components? Can I even achieve that in a large product?
Contract Testing solves these problems for you. Lets assume we have some micro-service X and a web front-end Y that communicates with it. The tools that implement Contract Testing let component Y document its expectations of the interface with X in a machine readable contract, frequently called a PACT.
How it works from Y’s point-of-view:
The unit/integration tests of Y define the PACT. Maybe its the same as the previous version, maybe it different. Both is fine.
They let the test framework dynamically generate a mock for X based on this PACT. Instead of needing to talking to a ‘real’ X, they talk to the mock.
The mock and Y’s own unit test together verify that Y really works according to the PACT. If the test fails, the team of Y has some fixing work to do and this process restarts. If the test passes, Y uploads the PACT to a broker and informs it which version of Y just validated successfully.
Whenever Y is deployed to production its CI/CD pipeline asks the broker if the current version of X in production has been validated against this PACT version. If not, Y’s deployment fails. If its fine, the pipeline informs the broker which version of Y is now deployed to production.
How it works from X’s point-of-view:
The unit/integration tests of X Start an instance of X running locally on some reachable network location.
It retrieves the PACT from the broker and uses the testing framework to generate a mock of Y. This mock sends requests to X based on the PACT and validates X’s responses match the PACT.
If the test fails, the team of X has some fixing work to do and this process restarts. If the test passed, X communicates to the broker that it was able to work with this version of the PACT.
Whenever X is deployed to production its CI/CD pipeline asks the broker if the current version of Y in production has been validated against this PACT version. If not, X’s deployment fails. If its fine, the pipeline informs the broker which version of X is now deployed to production.