Pact for Contract Testing
1. The problem we want to resolve:
If we wanted to test the application to determine whether it can communicate with other services, we could do one of two things:
- Deploy all microservices and perform end-to-end tests.
- Mock other microservices in unit/integration tests.
Advantages | Disadvantages | |
---|---|---|
Deploy all microservices and perform Integration test and end to end tests |
|
|
Mock other microservices in unit/integration tests |
|
|
To solve these issues, Contract Testing using Pact can be implemented. The main idea is to give you very fast feedback, without the need to set up the whole world of microservices. If you work on stubs, then the only applications you need are those that your application directly uses.
Contract Testing: Contract testing is immediately applicable anywhere where you have two services that need to communicate - such as an API client and a web front-end. In general, a contract is between a consumer (for example, a client that wants to receive some data) and a provider (for example, an API on a server that provides the data with the client needs). Contract testing really shines in an environment with many services (as is common for a microservice architecture).
Consumer: A client that wants to receive some data (for example, a web front end, or a message receiving endpoint).
Provider: A service or server that provides the data (for example, an API on a server that provides the data with the client needs, or the service that sends messages).
Pact: Pact is a contract testing tool. A contract between a consumer and provider is called a pact. Each pact is a collection of interactions. Each interaction describes:
An expected request - describing what the consumer is expected to send to the provider (this is always present for synchronous interactions like HTTP requests, but not required for asynchronous interactions like message queues)
- A minimal expected response - describing the parts of the response the consumer wants the provider to return.
Pact Interactions: Consumer Testing:
Consumer Pact tests operate on each interaction described earlier to say “assuming the provider returns the expected response for this request, does the consumer code correctly generate the request and handle the expected response?” |
---|
Using the Pact DSL, the expected request and response are registered with the mock service.
The consumer test code fires a real request to a mock provider (created by the Pact framework).
The mock provider compares the actual request with the expected request and emits the expected response if the comparison is successful.
The consumer test code confirms that the response was correctly understood
In contrast to the consumer tests, provider verification is entirely driven by the Pact framework. In provider verification, each request is sent to the provider, and the actual response it generates is compared with the minimal expected response described in the consumer test. |
---|
Provider verification passes if each request generates a response that contains at least the data described in the minimal expected response. If we pair the test and verification process for each interaction, the contract between the consumer and provider is fully tested without having to spin up the services together.
Pact Benefits and features:
- Supported Languages
Ruby
JVM
.NET
Go
Faster execution.
Reliable responses from mock service reduce the likelihood of flakey tests.
- Causes of failure are easier to identify as only one component is being tested at a time.
- Design of service provider is improved by considering first how the data is actually going to be used, rather than how it is most easily retrieved and serialised.
- No separate integration environment(s) required to be managed for automated integration tests - pact tests run in standalone CI builds.
- Integration flows that would traditionally require running multiple services at the same time can be broken down and each integration point tested separately.
- Pacts can be shared between providers and consumers using various different ways:
- By using Github/Bitbucket URL
- Use a pact Broker
- Use nexus and archive pacts as build artefacts
- Mockito can be used to produce a sample response while generating pacts.
Benefits of using Pact Broker:
- Pact Broker is an optional element of Contract testing, but thanks to it, you do not have to copy your contracts manually between services. Moreover, all interactions are stored in one place and are updated automatically.
- Versioning of different resources using the Pact broker
Pact Broker is a persistence layer for your contracts. It aggregates all interactions between services and draws them as a graph.
What is more, each interaction is presented in a human-readable form.
Local API Stubs using single docker container:
- Pact contracts are easily turned into locally running API stubs
Assuming you have a local directory containing your pact files at
./pacts
:docker pull pactfoundation/pact-stub-serverdocker run -t -p 8080:8080 -v "$(pwd)/pacts/:/app/pacts" pactfoundation/pact-stub-server -p 8080 -d pacts# Test your stub endpointscurl -v localhost:8080/some/api
Slack: https://hee-nhs-tis.slack.com/
Jira issues: https://hee-tis.atlassian.net/issues/?filter=14213