msw does not play well with jests describe blocks

Created on 6 Aug 2020  路  9Comments  路  Source: mswjs/msw

Environment

| Name | Version |
| ---- | ------- |
| msw | latest |
| node | 14.6.0 |
| OS | Win10 |

Request handlers

https://github.com/ljosberinn/personal-react-boilerplate/blob/master/src/client/i18n.test.ts#L21

Actual request

https://github.com/ljosberinn/personal-react-boilerplate/blob/master/src/client/i18n.ts#L209

Current behavior

Trying out this eslint rule which is considered best practice, at least by that plugin, I moved all jest hooks including setupServer into describe blocks.

However, it appears msw just stops working then. Any fetch request will not be intercepted.

If this is a known limitation, it'd be great if the docs mention it; I know the documented usage is outside, but that might also just be because describe isn't used in the docs at all.

Expected behavior

Continues working when setting up within describe.

Screenshots

I initially wrote the issue yesterday but accidentally didn't submit so forgive me here if I add screenshots later.

bug node

Most helpful comment

In different test cases, I append the same route, with different responses to test different API outcomes. From what you just said this is technically discouraged too?

No, what you are doing is perfectly fine. As @marcosvega91 has mentioned, just make sure to reset the handlers between the tests with server.resetHandlers(), so the changes you make per-test do not leak into irrelevant test suites.

For posterity, I'm going to include the solution that worked for you.

Solution

  • Declare a single instance of server (ideally, in jest's setupFilesAfterEnv configuration option).
  • Prepend necessary request handlers per test via server.use() API.
  • Don't forget to reset the runtime state of request handlers with server.resetHandlers(), usually in the afterEach jest hook.

All 9 comments

Hey, @ljosberinn. Thanks for raising this.

That sounds strange, as MSW itself is tested internally by both describe blocks and flattened test statements:

https://github.com/mswjs/msw/blob/4b2f057ee63f8b3a7afcca511d709f5691665756/test/msw-api/setup-server/scenarios/https.test.ts#L9-L28

Could you please try running your tests with --runInBand option of Jest? There may be a related issue, and if running in band fixes yours, then it'll help us triage. Thanks.

Testing more closely, it happens as soon as I add a second describe to the same file, where I repeat these setup steps:

  const server = setupServer();

  beforeAll(() => server.listen());

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

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

Having this in 2 describe blocks results in _all_ tests failing, even those that worked fine when using setupServer only in one (I skipped the others).

--runInBand didn't change anything except the test exeution time.

Example screenshot:

image

I tested it on this file, adjusting mockRoute to accept server as param:

interface MockRouteParams {
  language: string;
  server: ReturnType<typeof setupServer>;
  response?: Record<string, unknown> | string;
}

const mockRoute = ({ language, response, server }: MockRouteParams) => {
  server.use(
    rest.get(i18nEndpoint + language, (_req, res, ctx) => {
      if (!response) {
        return res();
      }

      if (typeof response === 'string') {
        return res(ctx.body(response));
      }

      return res(ctx.json(response));
    })
  );
};

As soon as I reset the file to its current git status:
image

Hi @ljosberinn I don't know If I understand your problem well.
Do you want to use

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

in each describe? 馃

right now I have:

const server = setupServer();

// lifecycle hooks here

describe('foo', () => {
  // tests **not** using msw
})

describe('bar', () => {
  // tests using msw
})

describe('baz', () => {
  // tests using msw
})

and this breaks:

describe('foo', () => {
  // tests **not** using msw
})

describe('bar', () => {
  const server = setupServer();

  // lifecycle hooks here

  // tests using msw
})

describe('baz', () => {
  const server = setupServer();

  // lifecycle hooks here

  // tests using msw
})

I'm afraid that at the moment creating multiple server instances is discouraged. Ideally, you should create a single server as a part of the _entire test run_ (in jest's setupFilesAfterEnv), and modify that instance by prepending request handlers and resetting them between tests.

Call of setupServer performs patching of necessary modules (http/XMLHttpRequest) without even calling server.listen(). The latter only activates the mocking by appending previously attached request handlers. This may be something we improve in the future, however.

@ljosberinn, would it work for you if you created a single server instance? If you think about it as a "server for all of my tests" and less like "server for this specific test", I hope it makes more sense to you. Let us know.

Yeah, fwiw it's possible to have multiple server across files, that works just fine. I'll just eslint disable the jest hooks rule in files with msw for now :)

I'm not sure whether I'm even using msw as intended then. In different test cases, I append the same route, with different responses to test different API outcomes. From what you just said this is technically discouraged too?

E.g. here and here. Or is that fine?

This is fine, you can add other handlers inside your tests. The only thing that you have to pay attention is to resetHandlers after each test to not affect other tests.

The only problem is to start two or more server in the same file.

Great, thanks!

In different test cases, I append the same route, with different responses to test different API outcomes. From what you just said this is technically discouraged too?

No, what you are doing is perfectly fine. As @marcosvega91 has mentioned, just make sure to reset the handlers between the tests with server.resetHandlers(), so the changes you make per-test do not leak into irrelevant test suites.

For posterity, I'm going to include the solution that worked for you.

Solution

  • Declare a single instance of server (ideally, in jest's setupFilesAfterEnv configuration option).
  • Prepend necessary request handlers per test via server.use() API.
  • Don't forget to reset the runtime state of request handlers with server.resetHandlers(), usually in the afterEach jest hook.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

PritamSangani picture PritamSangani  路  3Comments

baker-travis picture baker-travis  路  3Comments

abrudin picture abrudin  路  3Comments

mainfraame picture mainfraame  路  3Comments

danielstreit picture danielstreit  路  3Comments