MSW logging warnings for unhandled Supertest requests
Asked Answered
S

2

25

In my tests using Supertest and MSW I've noticed that, although they still pass successfully, MSW has started showing warnings for the requests that Supertest is making. For example (see files to reproduce at the end of the post):

$ npm t

> [email protected] test
> jest

 PASS  ./app.test.js
  password API
    ✓ exposes a number of words (76 ms)

  console.warn
    [MSW] Warning: captured a request without a matching request handler:

      • GET http://127.0.0.1:55984/api

    If you still wish to intercept this unhandled request, please create a request handler for it.
    Read more: https://mswjs.io/docs/getting-started/mocks

      at onUnhandledRequest (node_modules/msw/node/lib/index.js:7599:21)
      at node_modules/msw/node/lib/index.js:7630:13
      at fulfilled (node_modules/msw/node/lib/index.js:50:58)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.005 s
Ran all test suites.

The request GET http://127.0.0.1:55984/api is one that Supertest is making to the app, which is the whole point of the test, not one that MSW needs to be handling. These warnings weren't shown when I first wrote the tests, either.

The linked page shows how to create a handler, but I don't want MSW to handle these requests. Why did this start happening, and how can I stop it showing warnings for the "/api" calls?


package.json:

{
  "name": "msw-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.21.1",
    "express": "^4.17.1"
  },
  "devDependencies": {
    "jest": "^27.0.4",
    "msw": "^0.29.0",
    "supertest": "^6.1.3"
  }
}

app.js:

const axios = require("axios");
const express = require("express");

const app = express();

app.get("/api", (_, res) => {
  axios.get("https://api.pwnedpasswords.com/range/ABC12")
    .then(() => res.json({ words: 3 }))
    .catch((err) => res.sendStatus(500));
});

module.exports = app;

app.test.js:

const { rest } = require("msw");
const { setupServer } = require("msw/node");
const request = require("supertest");

const app = require("./app");

const server = setupServer(
  rest.get("https://api.pwnedpasswords.com/range/:range", (req, res, ctx) => {
    return res(ctx.status(200), ctx.text(""));
  }),
);

describe("password API", () => {
  beforeAll(() => server.listen());

  beforeEach(() => server.resetHandlers());

  afterAll(() => server.close());

  it("exposes a number of words", () => {
    return request(app).get("/api").expect(200).then((res) => {
      expect(res.body.words).toBe(3);
    });
  });
});
Spiegeleisen answered 17/6, 2021 at 18:42 Comment(0)
S
42

This feature was introduced in MSW v0.20.0, but in v0.29.0 the default setting for unhandled requests changed from "bypass" to "warn", hence the warnings suddenly appearing in the console. You can reset it to "bypass" as shown in the docs for setupWorker#start or setupServer#listen, in my case:

beforeAll(() => server.listen({ onUnhandledRequest: "bypass" }));

However, this might mean missing warnings for requests you should be handling, so another option is to pass a function that receives the request object. This could e.g. log a warning or throw an error (which will cause the test to fail). In my case, as all of my Supertest requests were to /api endpoints, that looked like:

beforeAll(() => server.listen({ 
  onUnhandledRequest: ({ method, url }) => {
    if (!url.pathname.startsWith("/api")) {
      throw new Error(`Unhandled ${method} request to ${url}`);
    }
  },
}));

As suggested by kettanaito in the comments, I looked into whether you could identify Supertest calls by their headers. Unfortunately it seems like Supertest no longer sets a default User-Agent, so you'd have to do this test-by-test:

describe("password API", () => {
  beforeAll(() => server.listen({ 
    onUnhandledRequest: ({ headers, method, url }) => {
      if (headers.get("User-Agent") !== "supertest") {
        throw new Error(`Unhandled ${method} request to ${url}`);
      }
    },
  }));

  beforeEach(() => server.resetHandlers());

  afterAll(() => server.close());

  it("exposes a number of words", () => {
    return request(app)
      .get("/api")
      .set("User-Agent", "supertest")
      .expect(200)
      .then((res) => {
        expect(res.body.words).toBe(3);
      });
  });
});

From v0.38.0 you can use the second parameter to onUnhandledRequest, conventionally named print, to hand control back to MSW in cases you don't want to handle, e.g.:

beforeAll(() => server.listen({ 
  onUnhandledRequest: ({ headers }, print) => {
    if (headers.get("User-Agent") === "supertest") {
      return;
    }
    print.error();
  },
}));
Spiegeleisen answered 17/6, 2021 at 18:42 Comment(2)
It's worth exploring if Supertest appends any custom request headers that may allow you to determine a request issued by Supertest more reliably.Algy
@Algy great idea! It looks like Superagent no longer sets a default User-Agent though, I'd have to do that in all of the tests.Spiegeleisen
W
25

To anyone using a worker and not a server - you can pass an object, with the 'onUnhandledRequest' field set to 'bypass', into the worker's 'start' method, like this:

worker.start({
    onUnhandledRequest: 'bypass',
});

Additional options included 'warn' and 'error' https://mswjs.io/docs/api/setup-worker/start#onunhandledrequest

Wines answered 28/8, 2022 at 16:29 Comment(1)
This works for Storybook too using msw-storybook-addon. Add this object to the initialize call like initialize({ onUnhandledRequest: 'bypass' });Mage

© 2022 - 2024 — McMap. All rights reserved.