Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSW logging warnings for unhandled Supertest requests

Tags:

supertest

msw

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);
    });
  });
});
like image 354
jonrsharpe Avatar asked Jun 17 '21 18:06

jonrsharpe


1 Answers

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();
  },
}));
like image 87
jonrsharpe Avatar answered Oct 14 '22 20:10

jonrsharpe