Testing your contracts (5/5)

Est Reading Time: 10 minutes

In parts 1-4 of this series, I’ve looked at what contract testing is, introduced the pact framework and showed how you can use it to create consumer side and provider side code to ensure a specific contract scenario.

Now I’m going to look at building pact in to your pipeline and tips on how to achieve this in an efficient way that doesn’t block or slow product delivery.

The Pact Broker

Pact broker is something I haven’t mentioned before, but it’s an excellent part of the pact framework and it is helps immensely adding pact to a pipeline.

What is it?

It’s a piece of software that sits between the consumers and providers and acts as a store for the contracts themselves. Let’s examine this be re-visiting the pact process:

  1. Consumer runs a unit test which hits a mock and generates a pact json file
  2. This file is passed to the provider
  3. The provider hits the real endpoint and verifies the response against the details in this file

When you have one consumer and one provider, this process could be managed easily enough with some kind of cloud or network based file storage. But what about when you have lots of providers and consumers? Even if you only have one of each, what do you do during development? How does the provider know which are the latest pacts and which are currently in development? You’d either have a complicated file hierarchy or a confusing naming convention or both!

The pact broker provides a simple interface and API that enable your providers and consumers to share pact files, verify the results and display them to the rest of the business. Much more detail on its purpose can be found at https://docs.pact.io/getting_started/sharing_pacts.

How do I run it?

There are many options for this ranging from getting the code and running locally, running in a container or hitting a version hosted elsewhere. Details can be found here https://github.com/pact-foundation/pact_broker.

The best way I have used before is hosting your own broker instance which can then be accessed by all your build systems.

How does it change the process?

If you were to use a pact broker, then the process at its simplest becomes:

  1. Consumer runs a unit test which hits a mock and generates a pact json file
  2. This file is uploaded to the pact broker
  3. Provider builds and downloads pacts from the pact broker where it is a provider
  4. The provider hits the real endpoints and verifies the responses against the details in these files

Adding pact to your pipeline

Now that we know a bit about the pact broker, we can look at integrating this in to our CI pipeline. It can be done without the pact broker, but the broker is easy to use and adds a lot of visibility to the process. We will look at this in a technology agnostic way so that regardless of the languages and technologies you use for your services and CI tools, hopefully you can take something from the process.

Consumer build

The first bit we will look at is the build pipeline for the consumer. At this point we want to ensure that when the consumer builds, it is generating the pact files and uploading them to the broker.

We have seen in the consumer code explanations how to create the pact generation so it runs when the unit tests run so we will assume that this part is already done. So how do we upload the pacts to the broker? This part is called publishing and handily there is a pact command line tool for this. It’s a ruby package called “pact_broker-client” and installation instructions can be found on the GitHub link above.

We can publish the pacts to our broker using the following command:

pact-broker publish "$pactdirectory" -b="https://example-pact-broker.co.uk" -t="$gitbranch" -a=“$VERSION"

This command uses the pact broker utility we installed earlier and publishes the JSON pact files produced by the build to the given broker. Let’s examine the arguments for the command:

  • Directory -> This is the folder that the pacts are published to. Every language of pact has a default which you can override when creating the pacts, so if you do that, then you need to provide that folder here too
  • URL -> Simply the url to your pact broker so this must be accessible to your CI system
  • Version -> This is used to tell the pact broker that this pact is for this version of the consumer

Tag

This is a really important part of the process, so let’s examine it in more details. Tags enable you to group pacts together and allow the provider to only run verified versions of the pacts. This means that during development of a new pact, you can upload it with various tags and not affect any new builds of the provider as it will not run the pacts with those tags. For the consumer CI, the best method we have found is to use your source control branch name as the tag. This means whatever your branching system, if the pact is under development you will have a way of keeping it separate from the live versions of the pacts.

Provider build

Now that we have our pacts publishing automatically to the broker, we will want to ensure that our provider is downloading the right pacts and verifying them during its own build. Brilliantly we don’t need a new command for this. In part 4, when we looked at the provider code, we saw how the provider pact methods run like integration tests, meaning that simply running the integration tests in your CI will run the pacts.

The only thing you want to ensure here is that when you run the integration tests you are downloading the pacts with the right tag. For the CI system, that tag should be the tag you use when deploying to live (see below). This is because we want to ensure at this stage that the provider changes will not break any of the live consumers. We use “prod” for this.

Deploying to live

Now we have the process in the consumer and the provider builds, we need to ensure that when we deploy to live we can also notify to the broker that a specific version of the pact is now live. This pact then becomes the go to pact for that consumer-provider pair to ensure that when a provider changes it is not breaking the live consumer.

How you fit this in to your specific CI system and scripts will of course vary, but either way you can use the pact broker command line utility again using the following command:

pact-broker create-version-tag -b="$pact_broker_url" -t="$environment" -a=“$consumer_name" -e=“$consumer_version"

This call creates a tag on a specific version of the pact. Let’s examine the arguments supplied to the call again:

  • URL -> Simply the url to your pact broker so this must be accessible to your CI system
  • Tag -> This is the tag itself – we use the above command in deploying to all environments and the tag is the environment name itself (live is named “prod”)
  • Name -> This is used to tell the pact broker that this tag is to be applied to this consumer only
  • Version -> This is used to tell the pact broker that this tag is to be applied to the pact of this version of the consumer only

Visibility

One of the big advantages that the pact broker gives is visibility of the pacts and the results of the pact verifications. The broker hosts a website which allows you to see a number of different things. The best way to investigate is to get a container up and running and have a play around, but the most important area is the home page which lists all the pacts between different consumer and provider pairs that have been uploaded:

Here you can see at a glance all your contracts, the last time they were published and the last time they were verified. This is excellent for any stakeholders who might want to see this kind of information.

Delving further in to each pact you can see the pact itself (the left icon on each line) which shows a physical representation of the latest JSON pact this consumer/provider pair has uploaded.

Clicking the right icon will show you are history of the pact verification:

This screen show every version of the pact that has been uploaded by the consumer and it shows every time that each one has been verified by a provider version. This screen is very useful for the history of a pact and can also show a development history of the pact as you can see executions against branch and in development versions too.

The broker actually works as a REST API too providing you the ability to view all data via API calls and delete if necessary as well. The website contains an API browser where this data can be visualised.

Development process

A big part of getting buy in from the team for contract testing is ensuring that it does not slow down or get in the way of the development process too much. Therefore the process of creating and testing new pacts is important to refine as you go along with the idea of having maximum confidence in the contracts without slowing down development.
This is the process I have used with the most success:

  1. Create the consumer tests on a branch
  2. Publish to broker, tagged to the branch
  3. Create the provider test on a branch
  4. Run against consumer branch tests – repeat 1-4 until passing
  5. Release consumer
  6. Release provider

This process ensures that new pacts are consumer led, but that the consumer won’t break the provider. An important part throughout this process is the communication between the consumer and the provider, which must be good at all times.

Summary

So, this is the end of the ridiculously large five parter on contract testing. We’ve looked at when contract testing is useful, what Pact is, how to write consumer code, how to write provider code and finally how to integrate it in to your CI system.

I think contract testing is an API world is vital and definitely something to look in to if you aren’t currently. It is not right for every scenario but I would definitely encourage you to have a look.

Share this post:

Leave any thoughts below!