Contract Testing using Pact
What is contract testing?
Contract testing is a methodology for ensuring that two separate systems (such as two microservices) are compatible and are able to communicate with one other. It captures the interactions that are exchanged between each service, storing them in a contract, which can then be used to verify that both parties adhere to it. Contract testing goes beyond schema testing, requiring both parties to come to a consensus on the allowed set of interactions and allowing for evolution over time.
What sets this form of testing apart from other approaches that aim to achieve the same thing, is that each system is able to be tested independently from the other and that the contract is generated by the code itself, meaning the contract is always kept up to date with reality.
The following diagram shows the key steps in contract testing:
How Contract testing works?
The consumer provides a contract which it is expecting from the provider and the contract tests passes only when the provider provides the data with the same contract. The contract is created by the consumer in a pact file. The pact file can be a simple json file or contract over http. In Case of autovoice the pact file was a json file. The provider reads the contract file and verifies the contract with the consumer. If the contract is same for both the consumer and the provider then the contract test passes. The Infrastructure contains the pact folder extension. The consumer makes use of the pactmaker that creates the pact file which has a method between which decides the name of the pact file based on the parameter we pass. The pact files defines the https interactions like request and response which is defined by the pactmaker. The provider makes use of the pact verifier to verify the pact file which is defined by the consumer. The pact verifier has a method UseEndpointTemplate to define the template with the provider. The contract test can be written in an Nunit test project and the test can be written as a separate unit test. The consumer test should be run first to create the pact file that is the reason we give an order(1) to the consumer test. The provider test should be run after the consumer test to verify the contract test so we order those test as order(2).
What are the benefits of contract testing?
Contract tests generally have the opposite properties to e2e integrated tests:
• They run fast, because they don't need to talk to multiple systems.
• They are are easier to maintain: you don't need to understand the entire ecosystem to write your tests.
• They are easy to debug and fix, because the problem is only ever in the component your testing - so you generally get a line number or a specific API endpoint that is failing.
• They are repeatable:
• They scale: because each component can be independently tested, build pipelines don't increase linearly / exponentially in time
• They uncover bugs locally, on developer machines: contract tests can and should run on developer machines prior to pushing code.
• The ability to develop the consumer (eg. a React Web App) before the API
• The ability to drive out the requirements for your provider first, meaning you implement exactly and only what you need in the provider.
• You get a set of well documented use cases ("Given ... a request for ... will return ...") that show exactly how a provider is being used.
• The ability to see exactly which fields each consumer is interested in, allowing unused fields to be removed, and new fields to be added in the provider API without impacting a consumer.
• The ability to immediately see which consumers will be broken if a change is made to the provider API.
Comments