logo logo

Integrating Consumer Contract Testing in Build Pipelines

main post image

In the previous articles, we discussed how we can use Pact for consumer-driven testing using JavaScript, Java and Spring Cloud. In this chapter, we will look into Consumer Driven Contract testing workflow in CI and also how to integrate with build pipelines.

Table of Contents

  1. Introduction to Consumer Contract Testing
  2. Consumer-Driven Contract Testing using Pact.js
  3. Consumer-Driven Contract Testing using Pact Java
  4. Consumer-Driven Contract Testing using Spring Cloud Contracts
  5. Event Driven Architecture: How to Perform Contract Testing in Kafka/PubSub
  6. You’re here → Integrating Contract Testing in Build Pipelines
    1. What is Pact-Broker?
    2. Consumer: Publishing Consumer Contract to Pact-Broker
    3. Provider: Verify Published Contract from Pact-Broker and Publish Results
    4. WebHooks
    5. Webhook Configuration
    6. CI Workflow in Jenkins

In this chapter we will extend the consumer and provider tests into the build pipeline, we will use Jenkins and the same can be done for any CI pipeline. We will heavily be using Pact-Broker to make sure Consumer and Provider don’t break each other.

What is Pact-Broker?

Pact Broker is an application for sharing for consumer driven contracts and verification results. It knows for each consumer version which provider has verified and the results published. It can be accessed via Web Interface or command line.

  • Responsibility of Provider CI  Don’t deploy the build if it breaks any of the consumer contracts.
  • Responsibility of Consumer CI Don’t deploy if it can’t consume provider API.

To get started, we need Jenkins (or any other CI tool), Pact-Broker and PostgreSQL up and running. For this, we have to setup a docker-compose file which starts Pact-Broker as localhost:8282 and Jenkins as localhost:8080

  1. Pact Broker is used to sharing contracts and verification results between Consumer and Provider.
  2. Prevents Consumer from being deployed if a contract was not successfully verified by the provider. This requires that new contract verification is executed whenever the contracts change.
  3. Prevents Provider from being deployed if not all the Consumer contracts were successfully verified.

Consumer: Publishing Consumer Contract to Pact Broker

Add @pact-foundation/pact-node to your package.json.

pactConsumerPublish.js

let pact = require('@pact-foundation/pact-node');
const path = require('path');

let opts = {
  pactFilesOrDirs: [path.resolve(__dirname, '../../../pacts/')],
  pactBroker: 'http://pact_broker.com:9292',
  pactBrokerUsername: '',
  pactBrokerPassword: '',
  consumerVersion: '1.0',
 tags: [‘prod’, ‘test‘]
};

pact
  .publishPacts(opts)
  .then(() => {
    console.log('Pact contract publishing complete!');
  })
  .catch((e) => {
    throw new Error('Pact contract publishing failed: ', e)
  });

Now we need to add this step to build pipeline to publish the contract:

stage('Publish Consumer Pact to Pact Broker') {
    steps {
            sh 'node src/rest-api/consumer/consumerPactPublish.js'
   }
}

Now every time the Jenkins job is built, a new pact version will be published to Pact-Broker if there are any changes to the contract or it will overwrite the existing contract and will be tagged with “Prod and Test”. The tag will help the provider know which version to be verified.

Consumer: Publishing Consumer Contract to Pact Broker

 

Provider: Verify Published Contract from PactBroker and Publish Results

pactProviderPublish.js

const { Verifier } = require('@pact-foundation/pact');
const { server, provider } = require('../provider');
const path = require('path');

var opts = {
  providerBaseUrl: 'http://localhost:8081',
  provider: 'validDate provider',
  pactBrokerUrl: 'http://localhost:8282',
  pactBrokerUsername: '',
  pactBrokerPassword: '',
  publishVerificationResult: true,
  consumerVersion: '2.0',
  providerVersion: '1.0',
  logLevel: 'DEBUG',
  timeout: 120000,
  tags: ["prod", "test"]
};

describe('Pact Provider verification', () => {
  it('Should validate the date consumer', async () => {
    const output = await new Verifier().verifyProvider(opts);
    console.log(output);
  });
});

Executing the above script will verify the provider service with the contract shared by the consumer and provider will publish the results back to the Pact-Broker.

Provider: Verify Published Contract from PactBroker and Publish Results

Let’s say the Provider breaks a contract or removes a field from the response, the contract test will fail and that in return will fail the build and stop the deployment.

Provider: Verify Published Contract from PactBroker and Publish Results

Now the Consumer build should know if the provider has verified the contracts successfully before deploying. The Pact CLI provides the can-i-deploy command that results in the same outcome when we integrate it into the build pipeline:

stage('Can-I-Deploy') {
         steps {
            sh 'pact-broker can-i-deploy --pacticipant=DateConsumer --broker-base-url=http://pact_broker.com:9292 --latest'
         }
      }

When the can-i-deploy step is executed, the build pipeline will fail as the provider verification failed.

Provider Verification

This stops the Consumer build to deploy.

 

WebHooks

Let’s take another case where the consumer published a new contract with a field from the contract removed, and now when the consumer runs the can-i-deploy command it would fail as the provider has not yet verified the latest version.

webhooks

Provider build pipeline

The provider tests need to run to make the consumer build pipeline Green. The easiest way to trigger the Provider build pipeline is to Create a Webhook that are executed only when the contract changes.

 

Webhook Configuration

Webhook configuration

Consumer can create a Webhook to trigger the provider build and verify the contract. The below POST call will create a WebHook with event contract content changed.

curl --location --request POST 'http://<packBrokerUrl>/webhooks/provider/validDate%20provider/consumer/DateConsumer' \
--header 'Content-Type:  application/json' \
--header 'Accept:  application/hal+json, application/json, */*; q=0.01' \
--header 'X-Interface:  HAL Browser' \
--header 'Content-Type: application/json' \
--data-raw '{
    "consumer": {
        "name": "DateConsumer"
    },
    "provider": {
        "name": "validDate provider"
    },
    "request": {
        "method": "POST",
        "url": "http://<jenkins>/job/provider-service/build",
        "headers": {
            "Accept": "application/json"
        }
    },
    "events": [
        {
            "name": "contract_content_changed"
        }
    ]
}’

The request would create a Webhook and the status in Pact Broker would change to Not run. Going forward for any changes to the contract from Consumer the Provider pipeline would be triggered.

For example: Consumer has made changes to the contract and published a new contract change. The provider pipeline is triggered and results are published.

Contract Testing

Now when the Consumer checks the provider contract verification using can-i-deploy the pipeline would still fail because the can-i-deploy check is executed before the provider finished its build and published the results.

One option is to add –retry-while-unknown and  –retry-interval as part of the can-i-deploy build step and then it would go green.

contract testing

The downside of this is that the Consumer has to wait until the Provider build is completed.

 

CI Workflow of Consumer in Jenkins

CI Workflow of Consumer in Jenkins

CI Workflow of Provider in Jenkins

CI Workflow of Provider in Jenkins

The entire code snippets can be found here.

In the next chapter, we will summarize all that we have learned on Contract Testing. Stay tuned!

Happy Testing 😎
Sai Krishna & Srinivasan Sekar

Avatar

About the author

Sai Krishna

Work at ThoughtWorks as Lead Consultant with 8 yrs of experience. Over the course of my career, I have worked on testing different mobile applications and building automation frameworks. Active contributor to Appium and also a member of Appium organization.

I love to contribute to open source technologies and passionate about new ways of thinking.

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Comments

7 1 comment

Leave a Reply

popup image

Best In Class Java, Python and C# SDK

Join thousands of automation developers using TestProject to supercharge open source testing, with a Selenium and Appium SDK, supporting Java, Python and .NET Core (C#)!
Sign Up Now right arrow
FacebookLinkedInTwitterEmail
Skip to toolbar