Tooling integration - MSW

Note

Official PactFlow On-Premises adapter

Create MSW (mock-service-worker) mocks and generate pact contracts from recorded interactions.

With @pactflow/pact-msw-adapter - npm

Notice

One of the quickest ways to see this in action in a full CI/CD flow, is to pick the MSW consumer, in our mix & match Bi-Directional Quick-Start Guide

Reference Links

Install

The following will install both msw and @pactflow/pact-msw-adapter

npm

npm install --save-dev msw @pactflow/pact-msw-adapter

yarn

yarn add --dev msw @pactflow/pact-msw-adapter

Define Mocks

Define your MSW Mocks

rest

MSW Quick Start Reference - Rest-API

const products = [
    {
      id: "09",
      type: "CREDIT_CARD",
      name: "Gem Visa",
    },
  ];

  const product =     {
    id: "09",
    type: "CREDIT_CARD",
    name: "Gem Visa",
  };

export const mock = { products, product }

graphql

MSW Quick Start Reference - GraphQL

Not currently tested, please feel free to help contribute or let us know if you want to see it!

Integrate MSW

MSW Quick Start Reference - Integrate MSW with your code

browser

MSW Quick Start Reference - Browser

// src/mocks/browser.js
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'

// This configures a Service Worker with the given request handlers.
export const worker = setupWorker(...handlers)

// https://mswjs.io/docs/api/setup-worker/use#examples
//
// Make the `worker` and `rest` references available globally,
// so they can be accessed in both runtime and test suites.
window.msw = {
    worker
  };

server

MSW Quick Start Reference - Node

import { setupServer } from 'msw/node'
import { handlers } from './handlers'

// Setup requests interception using the given handlers.
export const server = setupServer(...handlers)

See full example on GitHub

Setup pact-msw-adapter

We need to setup pact-msw-adapter in our test framework setup hooks.

Please read the reference guide, for up-to-date information on setupPactMswAdapter configuration options:

pact-msw-adapter Reference

browser

The adapter uses by default node’s filesystem to write pact files to disk. This makes it incompatible with browser environments where fs is not available. To overcome this, pact-msw-adapter allows for defining custom functions for writing files to disk.

This example uses Cypress's cy.writeFile method.

// / <reference types="cypress" />

import { setupPactMswAdapter } from '@pactflow/pact-msw-adapter';

let pactMswAdapter = undefined;

describe('Tests setupPactMswAdapter with msw works', async () => {
  beforeEach(() => {
    cy.visit('http://localhost:3000');
    if (!pactMswAdapter) {
      cy.window().then((window) => {
        pactMswAdapter = setupPactMswAdapter({
          worker: window.msw.worker,
          options: {
            consumer: 'testConsumer',
            timeout: 1000,
            providers: {
              ['testProvider']: ['/products'],
              ['testProvider2']: ['/product/09']
            },
            // pactOutDir: './pacts',
            // excludeUrl: ['static/'],
            includeUrl: ['/products', '/product/09'],
            excludeHeaders: ['ignore-me']
            // debug: true
          }
        });
        pactMswAdapter.newTest();
      });
    } else {
      pactMswAdapter.newTest();
    }
  });

  afterEach(() => {
    if (!pactMswAdapter) return;
    try {
      pactMswAdapter.verifyTest();
    } catch (err) {
      // cypress doesn't like errors on hooks...
      if (process.env.NODE_ENV !== 'production') {
        console.groupCollapsed(
          '%cError generating pacts.',
          'color:coral;font-weight:bold;'
        );
        console.log(err);
        console.groupEnd();
      } else {
        // fail on pipelines
        console.log(err);
        throw err;
      }
    }
  });

  after(async () => {
    if (!pactMswAdapter) return;

    try {
      await pactMswAdapter.writeToFile((path, data) => {
        console.log(JSON.stringify(data));
        cy.writeFile(path, data);
      });
    } catch (err) {
      console.groupCollapsed(
        '%cError generating pacts.',
        'color:coral;font-weight:bold;'
      );
      console.log(err);
      console.groupEnd();
      throw err;
    }
    pactMswAdapter.clear();
  });

See full example on GitHub

server

This example uses Jest

import { setupServer } from "msw/node";
import { setupPactMswAdapter } from "@pactflow/pact-msw-adapter";
import { handlers } from './mocks/handlers'

// This configures a request mocking server with the given request handlers.
const server = setupServer(...handlers);

const pactMswAdapter = setupPactMswAdapter({
  server,
  options: {
    consumer: "testConsumer", providers: { ['testProvider']: ['products'], ['testProvider2']: ['/product/10'] },
    debug: true,
    includeUrl: ['products', '/product'],
    excludeUrl: ['/product/11'],
    excludeHeaders: ["x-powered-by", "cookie"]
  },
});

beforeAll(() => {
    server.listen();
  });

  beforeEach(() => {
    pactMswAdapter.newTest();
  });

  afterEach(() => {
    pactMswAdapter.verifyTest();
    server.resetHandlers();
  });

  afterAll(async () => {
    await pactMswAdapter.writeToFile(); // writes the pacts to a file
    pactMswAdapter.clear();
    server.close();
  });

See full example on GitHub

Write your tests

No additional magic is needed. You can just start writing your tests.

browser

This example uses Cypress

it('should record a msw interaction and turn it into a back', () => {
  // Filter to the product we want
  cy.get('#input-product-search').type('Gem Visa');
  cy.get('.btn').click();
  cy.url().should('include', '/products/09');
  cy.contains('Gem Visa');
});

See full example on GitHub

server

This example uses Jest

import API from "./api";
import { mock } from './mocks/mockData'

describe("API", () => {
  test("get all products", async () => {
    const respProducts = await API.getAllProducts();
    expect(respProducts).toEqual(mock.products);
  });

  test("get product ID 09", async () => {
    const respProduct = await API.getProduct("09");
    expect(respProduct).toEqual(mock.product);
  });
});

See full example on GitHub

Run your tests in the usual manner for your framework.

Tip

You should now see a Pact file generated in your specified folder; the default is ./msw-generated-pacts - These are now ready for upload to the PactFlow On-Premises Platform.

Publication date: