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.