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
PactFlow On-Premises MSW Bi-Directional demo Project
Initial Proposal GitHub Issue
Install
Install MSW
Install @pactflow/pact-msw-adapter
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)
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:
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(); });
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(); });
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'); });
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); }); });
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.