question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Ability to capture/mock inbound requests in node (instead of outbound requests)

See original GitHub issue

Is your feature request related to a problem? Please describe. This is an entirely new feature for msw, but I think it would be awesome to be able to bootstrap a separate server process from my msw mock definitions.

Here’s the scenario I’m hitting: I often use mocks (via msw in browser) when developing a new feature and it works great! However, I’m also adding Cypress e2e testing and I’d like to use those same mocks to run against my tests.

I’ve done some extensive researching into how to use msw with Cypress, but there are a couple of issues I’ve run into:

  • Cypress cy.request bypasses any service workers. We use this to fetch some data in our e2e tests (rather than relying on the UI all of the time), but it does not return the mock data (as I would like) and instead bypasses msw.
  • I tried to patch the Cypress node process itself to use setupServer from msw/node, but I had issues getting that to patch Cypress properly (I think Cypress monkey patches the HTTP request function primitives themselves)
    • Even if I were to resolve this: I would run into issues due to the fact the memory context of the node process is separate from the one running in the service worker, so my changes would not be properly synchronized (my mock handlers write to an in-memory DB)

Describe the solution you’d like After banging my head against the wall for a while I realized all this could be resolved if I just moved away from the in-browser service worker model and used a separate, single nodejs/express server process that can respond with my mock data (and update in-memory DB). However, this is not easily doable with msw (currently).

My suggestion is to add this feature as a separate target for msw. The logic should not be too different from the existing targets: parse the request handlers for the mocks and generate new express routes that can be attached to any express app (or as a “connect” middleware).

Describe alternatives you’ve considered As previously mentioned, my goal is to be able to run Cypress with my same mocks as well as in the browser.

I really like the convenience of using msw’s service worker approach when just running the site locally (no need to spin up a separate server), but it has some limitations. Giving the developer the option to do both from the same set of mock definitions would be awesome!

Additional context Cypress plugin attempt (did not work):

on('task', {
  initMocks() {
    console.log('Initializing mocks');
    const server = setupServer(...handlers);
    server.listen();
    return null;
  },
});

I see this issue may be somewhat related, although it would still not solve the cy.request issue (as far as I can tell) https://github.com/mswjs/msw/issues/374

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:5
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
kettanaitocommented, Jul 21, 2021

Together with @idolize we’ve started the effort behind the http-middleware extension to MSW that would allow to spawn an HTTP server given a list of request handlers, or apply the response resolving logic to an existing HTTP server using a middleware.

1reaction
idolizecommented, Jan 19, 2021

Managed to get a proof of concept working (which I can turn into a PR if desired):

import { Headers, flattenHeadersList, headersToList } from 'headers-utils';
import { MockedRequest, RequestHandler } from 'msw';
import { RequestHandlersList } from 'msw/lib/types/setupWorker/glossary';
import { Application, RequestHandler as Middleware } from 'express';

const delay = (ms: number) =>
  new Promise<void>((resolve) => {
    setTimeout(() => {
      resolve();
    }, ms);
  });

// Using getResponse from https://github.com/mswjs/msw/blob/2b133436cd405a9ebf6d6882d5b8e47997b943f6/src/utils/getResponse.ts
export const getResponse = async <R extends MockedRequest, H extends RequestHandlersList>(
  req: R,
  handlers: H,
): Promise<ResponsePayload> => {
  // Sadly this is not exported on the library so we have to copy/paste here
  // ...
};

function setupMswMiddleware(app: Application, handlers: RequestHandler[]) {
  const mswMiddleware: Middleware = async (req, res, next) => {
    const headers = new Headers();
    for (let i = 0; i < req.rawHeaders.length; i += 2) {
      headers.set(req.rawHeaders[i], req.rawHeaders[i + 1]);
    }
    const url = req.protocol + '://' + req.get('host') + req.originalUrl;
    const newReq: MockedRequest = {
      url: new URL(url),
      method: req.method.toUpperCase(),
      headers,
      cookies: req.cookies, // TODO use cookie-parser
      mode: 'same-origin',
      keepalive: false,
      cache: 'default',
      destination: '',
      integrity: '',
      credentials: 'include',
      redirect: 'manual',
      referrer: req.header('referer'),
      referrerPolicy: '',
      body: req.body as { query: string; variables: Record<string, any> },
      bodyUsed: !!req.body,
      params: req.params,
    };
    const { handler, response } = await getResponse(newReq, handlers);
    if (handler) {
      // MSW Matched Request
      res.status(response.status);
      flattenHeadersList(headersToList(response.headers as any)).forEach(([key, value]) => {
        res.setHeader(key, value);
      });
      if (response.delay) {
        await delay(response.delay);
      }
      res.send(response.body);
    } else {
      // No MSW match - move along
      next();
    }
  }
  app.use(mswMiddleware);
}

export default setupMswMiddleware;

Then you just use it via:

import express from 'express';
import setupMocksMiddleware from './mswMiddleware';

const app = express();
app.use(express.json());
if (useMocks) {
  setupMocksMiddleware(app, myHandlers);
}
// add other routes etc...
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to mock requests for unit testing in Node
You've figured you need to mock out the outbound HTTP requests that your app is making, but you're not sure where to start....
Read more >
Node.js standard and urlfetch
All outbound requests in the "appengine" of "python" and "go" issue via "urlfetch" service. But for "node.js" it isn't available. There is improvements...
Read more >
What's benefits of nodejs mock module like nock?
The benefits of a network mocking library, such as Nock, come into play when your app is making outbound HTTP calls while handling...
Read more >
We achieved 10x performance using Node.js instead of PHP
Node.js is asynchronous by nature, and can process other requests without waiting for the incoming request to complete. This leads to more efficient...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found