Consumer-Driven Contract Testing (Pact-JavaScript)-2

Numanhan Duran
4 min readJun 2, 2023

Hello again everyone. In this article I will explain how to test two different microservices by giving an example. For these examples, I will explain how to publish test contracts that we create with Pact-Broker and Pact. Then we will try to test the compatibility of the services. If you’re ready, let’s start :)

What is Pact-Broker?

Pact-Broker is a service for storing, publishing and sharing Pact test contracts. Pact-Broker is used to manage contracts of microservices in a central place. Below is an example of storing and sharing contracts using Pact-Broker.

Example Scenario:

  • Suppose there are two microservices named “Product Service” and “Order Service”.
  • “Product Service” provides an API that returns a list of products.
  • “Order Service” works integrated with “Product Service” to create an order.

Step 1:

  • Pact-Broker Setup
    You can install Pact-Broker on your own server or as a cloud-based service. You can use a Pact-Broker container running on Docker or you can use a hosted service.

Step 2:

  • Creating and Publishing Agreements
    You create separate contract tests for “Product Service” and “Order Service” and publish them on Pact-Broker. For example:

Contract testing for “Product Service”:

const { Pact } = require('@pact-foundation/pact');
const axios = require('axios');

describe('Product Service Test', () => {
const provider = new Pact({
consumer: 'Consumer',
provider: 'Provider',
port: 1234,
pactBrokerUrl: 'http://pact-broker-url', // Pact Broker's URL
pactBrokerToken: 'pact-broker-token', // Add access key here if needed
});

before(() => provider.setup());
after(() => provider.finalize());

it('Product listing test', async () => {
// Create a contract expectation
const EXPECTED_PRODUCTS = [{ id: 1, name: 'Product 1' }, { id: 2, name: 'Product 2' }];
provider.addInteraction({
uponReceiving: 'product listing request',
withRequest: {
method: 'GET',
path: '/api/products',
},
willRespondWith: {
status: 200,
body: EXPECTED_PRODUCTS,
},
});

// Performing the API request
const response = await axios.get('http://localhost:1234/api/products');

// Confirming the expected response
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal(EXPECTED_PRODUCTS);

// Publish contract to Pact Broker
await provider.publishPacts();
});
});

Contract test for “Order Service”:

const { Pact } = require('@pact-foundation/pact');
const axios = require('axios');

describe('Order Service Test', () => {
const provider = new Pact({
consumer: 'Consumer',
provider: 'Order Service',
port: 1234,
pactBrokerUrl: 'http://pact-broker-url', // Pact Broker's URL
pactBrokerToken: 'pact-broker-token', // Add access key here if needed
});

before(() => provider.setup());
after(() => provider.finalize());

it('Order creation test', async () => {
// Create a contract expectation
const ORDER_REQUEST = { productId: 1, quantity: 2 };
const EXPECTED_ORDER = { id: 1, status: 'created' };
provider.addInteraction({
uponReceiving: 'request to create an order',
withRequest: {
method: 'POST',
path: '/api/orders',
body: ORDER_REQUEST,
},
willRespondWith: {
status: 200,
body: EXPECTED_ORDER,
},
});

// Performing the API request
const response = await axios.post('http://localhost:1234/api/orders', ORDER_REQUEST);

// Confirming the expected response
expect(response.status).to.equal(200);
expect(response.data).to.deep.equal(EXPECTED_ORDER);

// Publish contract to Pact Broker
await provider.publishPacts();
});
});

In each microservice project, after writing the contract test, we publish the contract to Pact-Broker with the provider.publishPacts() command.

Step 3:

  • Checking Contracts in Pact-Broker
    By using Pact-Broker we can check the contracts between “Product Service” and “Order Service”. For example, we can view these contracts from the Pact-Broker interface.

Step 4:

  • Compliance Testing of Microservices
    Suppose separate contracts are issued for both microservices and checked on Pact-Broker. Now, on each microservices project, we can run compatibility tests using the contracts of the other microservices.

For example, in the “Order Service” test, we can check for compatibility using the “Product Service” contract:

const { Pact } = require('@pact-foundation/pact');
const axios = require('axios');

describe('Order Service Test', () => {
const provider = new Pact({
consumer: 'Order Service',
provider: 'Product Service',
port: 1234,
pactBrokerUrl: 'http://pact-broker-url',
pactBrokerToken: 'pact-broker-token',
});

before(() => provider.setup());
after(() => provider.finalize());

it('Order service and Product service compatibility testing', async () => {
// Get previous version for contract check
const previousVersion = await provider.getLatestPact({ consumer: 'Order Service', provider: 'Product Service' });

// Contract check
const response = await axios.get('http://localhost:1234/api/products');

// Verify contract control
const result = await provider.verify();

// Verify compliance status in contract check
expect(result.ok).to.be.true;
});
});

In this example, the “Order Service” microservice tests compliance using the contract issued by “Product Service”.

Using Pact-Broker enables compliance tracking between microservices, management of contracts in a central location, and early detection of compliance issues.

For now that’s all I implemented and tried to understand. This series will be continue. Don’t forget to follow and clap. See you in next articles :)

https://www.linkedin.com/in/numanhanduran

--

--