Consumer-Driven Contract Testing with Pact – The basics

What is contract testing?

Contract testing was coined by Martin Fowler and he has a great article on his bliki describing how to it works.

I recommend you read that article first if you don’t know what contract testing is or what it’s used …


This content originally appeared on DEV Community and was authored by Paul-Sebastian Manole

What is contract testing?

Contract testing was coined by Martin Fowler and he has a great article on his bliki describing how to it works.

I recommend you read that article first if you don't know what contract testing is or what it's used for.

This article does not describe how it works. Instead it tries to be a practical step by step explanation of how Pact and PactFlow work. At the end there will be a link to some code for an even more practical example.

Hopefully it will be easier and faster to understand how Pact and PactFlow work by reading this article instead of the documentation, but if you really want to get into depth, the official documentation is still the best place to go.

What is Pact and Pact CDC testing?

Pact is an open source Contract Testing tool.

Consumer-Driven Contract Testing (CDC) is one way that Pact implements contract testing.

In principle it has two major steps:

  1. First a contract definition, validation and generation at the consumer, and
  2. A final contract validation at the provider.

How does it work?

The goal for Pact CDC is to capture the expectations of the consumer of a provided service into what's called a "pact", which is basically a contract written in code.

The contract contains interactions.

Interactions are a declarative way to encode the messages (requests and responses) that are exchanged between a consumer and a provider for a particular purpose, for example for a products service provider to allow for product creation.

CDC testing starts at the consumer.

In unit tests called Consumer Pact tests, the consumer declares the interactions and adds them to a provider mock coming from Pact, the Pact Mock Provider.

The interactions contain information like:

  • request payload, format, method (e.g., GET), path (.e.g., /users), headers, cookies, etc.,
  • response payload, format, response code, headers, etc.

For HTTP APIs typical interactions include the typical response codes: 201 for creating new resources, 200 for fetching existing resources, 400 for badly formatted requests, 404 for inexistent resources, 500 for various errors, etc.

Practical example

Let's assume we have a contract between a React application, which is the consumer, and an HTTP API, which is the provider.

Typically, the React application would have unit tests for interacting with its providers. The tests would validate some sort of an API provider class (or service class), or individual data fetcher functions or hooks, or increasingly more popular nowadays, TanStack Query query hooks and mutation hooks.

These code units are meant to abstract away the complexities of dealing with the network, calling the upstream service, handling errors, and mapping the raw responses to types that the application understands and uses. They would typically use something like Mock Service Worker (MSW) to mock the responses that the upstream API would return and test that the code can correctly handle those responses (which usually means doing data mapping, schema validation of the deserialized data, error handling, etc.).

But, as you can tell, this does not ensure a successful integration. In production, a different version of the provider could be deployed which does not respond in the same way as it was mocked in the unit tests. Read on to find out contract testing fixes this.

With Pact Consumer tests, Pact generates the MSW part, instead of the developer having to write it. Pact effectively generates a mock of the provider, called a Pact Mock Provider, using the declared interactions.

Thus a previous unit test like CustomerService.spec.ts becomes CustomerServiceContract.spec.ts or CustomerService.pact.spec.ts, to denote that Pact and contract testing is used. The unit tests remain largely the same, except that Pact takes care of the MSW mocking and intercepts the network calls.

If the tests pass, Pact will generate pact files containing the definition of the interactions. There's no reason to generate the pact files before validating the consumer side in Pact CDC testing anyway.

Even though generating the pact files means that the consumer has validated its side of the contract (or that the consumer has validated the contract, depends on how you look at it), in Pact terms this is not a validated contract yet, until the provider also validates it.

Contract testing is about validating the interactions between a consumer and a provider, and specifically, it's about saying which versions of a consumer and a provider respect the same contract and can safely be deployed together.

Obviously the pact now also needs to be validated by the provider, which means that given the same interactions, the provider must be able to handle the same requests and send the same expected responses that the mock provider previously sent in the consumer unit tests in place of the actual provider.

Pact also helps make this easier. It takes the pact files and generates a mock of the consumer. The provider uses it in unit tests to validate the contract from the provider's perspective.

But before I explain how that works, I need to explain PactFlow first, because it plays a central role in orchestrating everything I've described so far.

PactFlow

In order to orchestrate everything that's needed for contract testing and validation, as well as deployment to production, you need to have a Pact Broker. PactFlow is the most widely used Pact Broker as a Service.

Usual setups integrate PactFlow into the CI pipeline:

Whenever a consumer pushes a change and triggers CI, what typically happens is:

  1. CI executes the unit tests, including Consumer Pact tests.
  2. If the consumer tests pass, they generate the pact files containing the contracts.
  3. CI uploads the pact files and test results to PactFlow, where they are versioned.

Similarly, if a provider triggers CI, what typically happens is:

  1. CI executes the unit tests, including Provider Verification tests which download all the pacts referring to the provider and verify that the provider respects those contracts.
  2. The provider verification tests use a verifier coming from Pact which will tell the broker which pacts where validated and which were not.
  3. The broker marks the consumer-provider pair and their contract as valid for deployment or not.

The versioned data stored in PactFlow together with the current system deployment status can tell us whether a new version of a consumer can be deployed to production alongside an existing deployed provider, or it can tell us if the latest versions of a consumer and provider can be deployed together at the same time, etc.

In short, the current system deployment status and the latest contract testing information can be reduced and diff'ed together to tell us which of the latest versions of different artifacts can be deployed safely.

PactFlow does this through an internal tool called 'can-i-deploy'. It's the last step before deployment and acts as a deployment safety gate.

Practical example (continued)

Like I mentioned before I had to explain PactFlow, the pact also needs to be validated by the provider.

Pact CDC testing is much simpler on the provider side (mainly because no interactions need to be declared, just tested).

The provider also needs to write a unit test called a Provider Verification Test, to validate each interaction, but Pact provides a verifier helper here, which does most of the work: it downloads the pacts from the broker and replays the declared consumer requests, sending them to the provider listening on localhost. All the provider tests have to do is to set up the provider to listen to those requests and respond to them.

The Pact verifier checks if the provider responds with the expected response as declared in the interaction, and communicates that back to the Pact Broker.

And that is basically the end of contract testing and validation.

For a practical code example see my other article.

Summary and Benefits

Consumer teams can create and maintain Pact contracts independently of the provider teams. This allows consumers to define their requirements and expectations without being reliant on provider implementation details.

Pact contract tests are typically integrated into the continuous integration (CI) pipelines of both consumers and providers. Whenever changes are made to either side, the tests are automatically run to ensure that the contracts are still in alignment, facilitating seamless integration and continuous delivery.


This content originally appeared on DEV Community and was authored by Paul-Sebastian Manole


Print Share Comment Cite Upload Translate Updates
APA

Paul-Sebastian Manole | Sciencx (2024-08-05T01:52:05+00:00) Consumer-Driven Contract Testing with Pact – The basics. Retrieved from https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/

MLA
" » Consumer-Driven Contract Testing with Pact – The basics." Paul-Sebastian Manole | Sciencx - Monday August 5, 2024, https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/
HARVARD
Paul-Sebastian Manole | Sciencx Monday August 5, 2024 » Consumer-Driven Contract Testing with Pact – The basics., viewed ,<https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/>
VANCOUVER
Paul-Sebastian Manole | Sciencx - » Consumer-Driven Contract Testing with Pact – The basics. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/
CHICAGO
" » Consumer-Driven Contract Testing with Pact – The basics." Paul-Sebastian Manole | Sciencx - Accessed . https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/
IEEE
" » Consumer-Driven Contract Testing with Pact – The basics." Paul-Sebastian Manole | Sciencx [Online]. Available: https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/. [Accessed: ]
rf:citation
» Consumer-Driven Contract Testing with Pact – The basics | Paul-Sebastian Manole | Sciencx | https://www.scien.cx/2024/08/05/consumer-driven-contract-testing-with-pact-the-basics/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.