Skip to main content

15 minute path to nirvana

This getting started guide has the intention to get you across the key concepts quickly. As we go through, there will be code snippets to demonstrate concepts. These are designed to be runnable and should help you get started, and to assist with there explanation. When there is code to run, you can fork the real code clone it to your machine, and will you will see the output of each step.

Tasks

  • Step 01 - Write your first pact - See folder ./Step_01_WriteYourFirstPact
  • Step 02 - Verify your first pact locally - See folder ./Step_02_VerifyYourFirstPactLocally
  • Step 03 - Get a pact broker - See folder ./Step_03_GetAPactBroker
  • Step 04 - Publish your first pact locally - See folder ./Step_04_PublishYourFirstPactLocally
  • Step 05 - Verify your first pact from a broker locally - See folder ./Step_05_VerifyYourFirstPactFromABrokerLocally
  • Step 06 - Publish your first pact from CI - See folder ./Step_06_PublishYourFirstPactFromCI
  • Step 07 - Verify your first pact from CI - See folder ./Step_07_VerifyYourFirstPactFromCI
  • Step 08 - Add can-i-deploy for Provider - See folder ./Step_08_AddCanIDeployForProvider
  • Step 09 - Add record-deployment for Provider - See folder ./Step_09_AddRecordDeploymentForProvider
  • Step 10 - Add can-i-deploy for Consumer - See folder ./Step_10_AddCanIDeployForConsumer
  • Step 11 - Add record-deployment for Consumer - See folder ./Step_11_AddRecordDeploymentForConsumer
  • Step 12 - Add webhook for Provider CI when Consumer changes pact - See folder ./Step_12_AddWebhookForProviderCIWhenConsumerChanges
  • Step 13 - Complete Consumer & Provider build - See folder ./Step_13_CompleteBuild

Step 01 - Write your first pact

See folder ./Step_01_WriteYourFirstPact

An example scenario: Order API

Here we have an example describing Pact tests between a consumer (Order Web), and its provider (the Order API).

In the Consumer project, we're going to need:

  • A model (the Order class) to represent the data returned from the Order API
  • A client (the OrderApiClient) which will be responsible for making the HTTP calls to the Order API and returning an internal representation of an Order.

Note that to create a pact, you do need to write the code that executes the HTTP requests to your service (in your client class), but you don't need to write the full stack of consumer code (eg. the UI).

Testing the Order Web (consumer) project

Scope of a Consumer Pact Test

Ideally, the Pact tests should be "unit tests" for your client class, and they should just focus on ensuring that the request creation and response handling are correct. If you use pact for your UI tests, you'll end up with an explosion of redundant interactions that will make the verification process tedious. Remember that pact is for testing the contract used for communication, and not for testing particular UI behaviour or business logic.

Usually, your application will be broken down into a number of sub-components, depending on what type of application your consumer is (e.g. a Web application or another API). This is how you might visualise the coverage of a consumer Pact test:

Scope of a consumer Pact test

Here, a Collaborator is a component whose job is to communicate with another system. In our case, this is the OrderApiClientcommunicating with the external Order Api system. This is what we want our consumer test to inspect.

1. Start with your model

Imagine a simple model class that looks something like this (order.js).

Step_01_WriteYourFirstPact/consumer/order.js
loading...

The attributes for an Order live on a remote server, and will need to be retrieved by an HTTP call to the Order API.

Step_02_VerifyYourFirstPactLocally/provider/data/orders.js
loading...

2. Create an Order API client

Here we have our external collaborator client. Its job is to both make the external request to the Order API and convert the response into the internal Order model as per above:

Step_01_WriteYourFirstPact/consumer/orderClient.js
loading...

3. Configure the mock Order API

The following code will create a mock service on localhost:1234 which will respond to your application's queries over HTTP as if it were the real Order API. It also creates a mock provider object which you will use to set up your expectations.

Step_01_WriteYourFirstPact/pact.js
loading...

4. Write a test

Step_01_WriteYourFirstPact/consumer/consumer.spec.js
loading...

5. Run the test

We will run the npm run test:consumer command

Step_01_WriteYourFirstPact/package.json
loading...

Hopefully this should be green!

Running the passing Order API spec will generate a pact file in the configured pact dir (./pacts by default). Logs will be output to the configured log dir (./log by default) that can be useful when diagnosing problems.

You now have a pact file that can be used to verify your expectations of the Order API provider project.

Now, in real life you would rinse and repeat for other likely status codes that may be returned. For example, consider how you want your client to respond to a:

  • 404 (return null, or raise an error?)
  • 400 (how should validation errors be handled, what will the body look like when there is one?)
  • 500 (specifying that the response body should contain an error message, and ensuring that your client logs that error message will make your life much easier when things go wrong. Note that it may be hard to force your provider to generate a 500 error on demand. You may need to collaborate with your provider team to create a known provider state that will artificially return a 500 error, or you may just wish to use a standard unit test without a pact to test this.)
  • 401/403 if there is authorisation.

Run the consumer Tests!

OK enough talk - let's run the consumer test. If you like, click around the project to see the files from above in context. The most interesting file is the consumer test in ./consumer/consumer.spec.js .

You should have a file that looks like this - this is our pact file (lowercase p) - Pact is our framework, that produces pact files.

It contains the details captured in your consumer test.

Step_02_VerifyYourFirstPactLocally/pacts/GettingStartedOrderWeb-GettingStartedOrderApi.json
loading...

Step 02 - Verify your first pact locally

See folder ./Step_02_VerifyYourFirstPactLocally

Sharing the contracts with the provider team

Now that you have created and run your consumer tests, produced a contract (the pact file) as an artefact, we need to share it with the team responsible for managing the Order API, so that they can confirm they meet all of the expectations set in it.

There are multiple ways to share pacts, but the recommended approach is to use a Pact Broker as it enables powerful automation workflows.

At this point, we just want to get our first test verified on the provider side, so we can build confidence.

We will introduce out Pact Broker at a later stage.

Testing the Order API (provider) project

Scope of a Provider Pact Test

On the Provider side, Pact needs to replay all of the interactions (usually HTTP requests) against your service. There are a number of choices that can be made here, but usually these are the choices:

  • Invoke just the controller layer (in an MVC app, or the "Adapter" in our diagram) and stub out layers beneath
  • Choosing a real vs mocked out database
  • Choosing to hit mock HTTP servers or mocks for external services

Generally speaking, we test the entire service and mock out external services such as downstream APIs (which would need their own set of Pact tests) and databases. This gives you some of the benefits of an integration test without the high costs of maintenance.

This is how you might visualise the coverage of a provider Pact test:

Provider side Pact test scope

1. Create the Order API

Below we have created a simple API using Express JS.

Step_02_VerifyYourFirstPactLocally/provider/provider.js
loading...

The attributes for an Order in our provider API. This is a simple in-memory database. The principles are the same if you are using any type of data-store

Step_02_VerifyYourFirstPactLocally/provider/data/orders.js
loading...

2. Setup your provider verification tests

We now need to perform the "provider verification" task, which involves the following:

  1. Telling Pact where to find the contract files, and where the Order API will be running (lines 3-13)
  2. Starting the API (line 16-18)
  3. Running the Provider verification task (line 22)

We mentioned earlier that we are sharing this file directly with the provider, we will set the provider up so it knows where this file is

Step_02_VerifyYourFirstPactLocally/pact.js
loading...

This is our verification task for a provider, it is designed to run every time a consumer pact file contents change.

We will build out a seperate verification task that runs every time the provider codebase changes later, the important thing to note is that we can load a pact file into our broker from a file path, or from a remote location.

Step_02_VerifyYourFirstPactLocally/provider/provider.spec.js
loading...

3. Run the Provider tests

We will run the npm run test:provider command

Step_02_VerifyYourFirstPactLocally/package.json
loading...

Run the Provider tests

Let's run the provider test. If you like, click around the project to see the files from above in context. The most interesting file is the consumer test in ./provider/provider.spec.js .

We will run the npm run test:provider command

Step_02_VerifyYourFirstPactLocally/package.json
loading...

Success! Our pact is correctly verified and we know that our consumer and provider are compatible.

You can play around with the codebases now

  • try removing a property from the returned order and run the provider verification task
  • try changing the endpoint the consumer users

both of these should fail!

Step 03 - Get a pact broker

See folder ./Step_03_GetAPactBroker

Sharing the contracts with the provider team via a broker

Sharing is caring

Now that you have created and run your consumer tests, producing a contract (the pact file) as an artefact. You've shared it with the team responsible for managing the Order API. They can confirm they meet all of the expectations set in it.

There are multiple ways to share pacts, but the recommended approach is to use a Pact Broker as it enables powerful automation workflows.

There are two flavours.

  • The Pact Broker is an open source tool that requires you to deploy, administer and host it yourself.
  • The Pactflow Broker is plug-and-play option, that we've created Pactflow, a fully managed Pact Broker with additional features to simplify teams getting started and scaling with Pact.

To get started for free, you can sign up to our Developer Plan here.

We will get started with the Open source broker, at this point in the demo, and once you have a full workflow, we will get you setup with a free hosted Pact Broker from pactflow.io.

Copy this file and run docker-compose up -d, once it is running you can visit it at http://localhost:8000 and login with pact_workshop/pact_workshop

Step_03_GetAPactBroker/docker-compose.yml
loading...

Step 04 - Publish your first pact locally

See folder ./Step_04_PublishYourFirstPactLocally

We will use one of pact cli tools

Step_04_PublishYourFirstPactLocally/Makefile
loading...

Step 05 - Verify your first pact from a broker locally

See folder ./Step_05_VerifyYourFirstPactFromABrokerLocally

Once your pact is uploaded, grab the pact file URL.

run you test with PACT_URL=your_url npm run test:provider

We will setup this task to run later via a webhook, so that it runs when the consumer pact file changes. We can publish our results back to the broker.

However we would normally do that via CI, you can trigger it by setting an env var

PACT_URL=your_url PACT_BROKER_PUBLISH_VERIFICATION_RESULTS=true npm run test:provider

Step_05_VerifyYourFirstPactFromABrokerLocally/provider/provider.spec.js
loading...

As verification results can affect things, lets run it from CI instead, so every time we make a change in our codebase, we can upload our pacts to the broker, and verify them

Step 06 - Publish your first pact from CI

See folder ./Step_06_PublishYourFirstPactFromCI

We need a CI workflow

Step_06_PublishYourFirstPactFromCI/.github/workflows/build.yml
loading...

We run it via a script, that way we can move CI providers easily, Pact works in any CI/CD system

Step_06_PublishYourFirstPactFromCI/Makefile
loading...

Step 07 - Verify your first pact from CI

See folder ./Step_07_VerifyYourFirstPactFromCI

We need a workflow file for our provider, it looks very similar to our consumer ci, this workflow file will run every time the provider code changes

Step_07_VerifyYourFirstPactFromCI/.github/workflows/build.yml
loading...

We run it via a script, that way we can move CI providers easily, Pact works in any CI/CD system.

You will note there are two tasks, test_contract_requiring_verification & test_provider_change

Step_07_VerifyYourFirstPactFromCI/Makefile
loading...

Lets update our original provider verification task to read from our Pact broker, this is our test_contract_requiring_verification

Step_07_VerifyYourFirstPactFromCI/provider/provider.consumerChange.spec.js
loading...

We are going to create a new provider CI task, this is designed to protect our provider, when they change

Step_07_VerifyYourFirstPactFromCI/provider/provider.providerChange.spec.js
loading...

Lets update our setup file, so we only run the specified test files

Step_07_VerifyYourFirstPactFromCI/package.json
loading...

Step 08 - Add can-i-deploy for Provider

See folder ./Step_08_AddCanIDeployForProvider

Lets run can-i-deploy

Step_08_AddCanIDeployForProvider/Makefile
loading...

Lets add it to our pipeline

Step_08_AddCanIDeployForProvider/.github/workflows/build.yml
loading...

Step 09 - Add record-deployment for Provider

See folder ./Step_09_AddRecordDeploymentForProvider

Step_09_AddRecordDeploymentForProvider/Makefile
loading...

Lets add it to our pipeline

Step_09_AddRecordDeploymentForProvider/.github/workflows/build.yml
loading...

Step 10 - Add can-i-deploy for Consumer

See folder ./Step_10_AddCanIDeployForConsumer

Step_10_AddCanIDeployForConsumer/Makefile
loading...

Lets add it to our pipeline

Step_10_AddCanIDeployForConsumer/.github/workflows/build.yml
loading...

Step 11 - Add record-deployment for Consumer

See folder ./Step_11_AddRecordDeploymentForConsumer

Step_11_AddRecordDeploymentForConsumer/Makefile
loading...

Lets add it to our pipeline

Step_11_AddRecordDeploymentForConsumer/.github/workflows/build.yml
loading...

Step 12 - Add webhook for Provider CI when Consumer changes pact

See folder ./Step_12_AddWebhookForProviderCIWhenConsumerChanges

Step_12_AddWebhookForProviderCIWhenConsumerChanges/Makefile
loading...

Lets add it to our pipeline

Step_12_AddWebhookForProviderCIWhenConsumerChanges/.github/workflows/contract_requiring_verification_published.yml
loading...

Step 13 - Complete Consumer & Provider build

See folder ./Step_13_CompleteBuild

You are all complete, you now have a fully operationalised consumer and provider CI/CD workflow protected by Pact.

Step_13_CompleteBuild/Makefile
loading...