(MSW + React-Query + Vitest) Requests occurring in prior test fail in subsequent test
Asked Answered
U

1

2

I have built a ReactJS application using Vite, and I am testing my application using Vitest.

I make heavy usage of react-query within my application. For mocking network requests during testing, I utilize MSW.

I am dealing with a situation where network requests which are certainly executed by code in the first test do not fail until the second test, and I am not sure how to prevent this from happening. It certainly seems to be a race condition, since requests are executed asynchronously while the application is rendered during the first test, and it seems as if some are inevitably "interrupted" when the second test begins and this consistently results in errors being logged (even though both tests pass)

describe("Test the view", () => {
  beforeEach(() => {
    cleanup();
    server.use(...);
  });

  it("DEBUG Handles when rendered with a malformed uuid",
    async () => {
      const uuid= "fail";

      consoleErrorSpy.mockImplementation(() => {
        return;
      });
      server.use(...);
      render(wrappedDetails(uuid));

      await waitFor(
        () => {
          expect(
            screen.getByText("Encountered an error while fetching")
          ).toBeInTheDocument();
        },
        { timeout: 10000 }
      );
    },
    { timeout: 10000 }
  );

  it("DEBUG Displays the details that it is supposed to", async () => {
    const uuid= faker.datatype.uuid();

    render(wrappedDetails(uuid));
    server.use(...);

    await waitFor(() => {
      expect(screen.getAllByRole("heading", { level: 3 }).length).toBe(1);
    });
  });
});

The wrappedDetails that both tests render is like so:

function wrappedDetails(uuid: string) {
  return (
    <MemoryRouter initialEntries={["/things/" + uuid]}>
      <QueryClientProvider client={queryClient}>
        <GenericToastProvider>
          <Routes>
            <Route path="/things/:uuid" element={<ThingDetails />}></Route>
          </Routes>
        </GenericToastProvider>
      </QueryClientProvider>
    </MemoryRouter>
  );
}

Unavoidably I end up with the following output:

> [email protected] qtest
> VITE_TESTING=true vitest run "-t" "DEBUG" "--no-threads"


 RUN  v0.29.3 /path/to/tests

stderr | src/tests/DetailsView.test.tsx > Test the Details view > Displays the details that it is supposed to
GET /api/thing/fail/task-definitions/ net::ERR_FAILED
GET /api/thing/fail/tasks/ net::ERR_FAILED

stderr | unknown test
Error: Error: connect ECONNREFUSED 127.0.0.1:3000
    at Object.dispatchError (/home/username/manager-ui/project-manager-ui/node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js:63:19)
    at Request.<anonymous> (/home/username/manager-ui/project-manager-ui/node_modules/jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js:655:18)
    at Request.emit (node:events:539:35)
    at ClientRequest.<anonymous> (/home/username/manager-ui/project-manager-ui/node_modules/jsdom/lib/jsdom/living/helpers/http-request.js:121:14)
    at ClientRequest.emit (node:events:527:28)
    at Socket.socketErrorListener (node:_http_client:454:9)
    at Socket.emit (node:events:527:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) undefined
Error: Error: connect ECONNREFUSED 127.0.0.1:3000
    at Object.dispatchError (/home/username/manager-ui/project-manager-ui/node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js:63:19)
    at Request.<anonymous> (/home/username/manager-ui/project-manager-ui/node_modules/jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js:655:18)
    at Request.emit (node:events:539:35)
    at ClientRequest.<anonymous> (/home/username/manager-ui/project-manager-ui/node_modules/jsdom/lib/jsdom/living/helpers/http-request.js:121:14)
    at ClientRequest.emit (node:events:527:28)
    at Socket.socketErrorListener (node:_http_client:454:9)
    at Socket.emit (node:events:527:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) undefined

 ✓ src/tests/thingDetailsView.test.tsx (41) 651ms

 Test Files  1 passed | 9 skipped (10)
      Tests  2 passed | 118 skipped (120)
   Start at  16:13:28
   Duration  9.43s (transform 186ms, setup 2.68s, collect 5.59s, tests 651ms)

Of particular note are the lines:

stderr | src/tests/DetailsView.test.tsx > Test the Details view > Displays the details that it is supposed to
GET /api/thing/fail/task-definitions/ net::ERR_FAILED
GET /api/thing/fail/tasks/ net::ERR_FAILED

...These are identified as happening during the second test but the second test makes no use of a uuid "fail" (that is the first test) which is what these failing requests are clearly based on, i.e. /api/thing/fail/task-definitions/ and /api/thing/fail/tasks/ and I am running out of ideas of how to make this stop.

I understand if clarification is needed in order to be able to help. I will respond quickly to any questions asked...

UPDATE 1 - 3/23/2023

A commenter asked whether I was resetting the server handlers after each test, and the answer is that yes, I am. I have the following simple setup.ts file configured:

// Polyfill "window.fetch" used in the React component.
import "whatwg-fetch";
import "@testing-library/jest-dom";

import { server } from "../mocks/server";

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

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

afterAll(() => {
  server.close();
});
Usually answered 20/3, 2023 at 20:34 Comment(6)
Are you resetting the server handlers after each test?Kelsey
Hi @TomasVancoillie, thanks for reaching out. I have included an update above to address your question but the short answer is "yes, I am"Usually
You probably should turn off network error logging as stated in the docs.Pastose
Interesting thought @ivanatias, but I attempted that and the logs would appear anyways. It seems the logs are coming from JSDOM not react-query, so what I really need would be a way to reset JSDOM in between tests that are in the same file, whereas the only method I could find to reset JSDOM was to break tests apart into individual files which quickly becomes unwieldy.Usually
This question/answer seems to be related. It's about the sequence of setup: https://mcmap.net/q/1633901/-connect-econnrefused-when-using-msw-with-react-testing-library-nextjs-swr/14072498Steel
Thanks @RoarS. I am currently calling server.listen() in the beforeAll as part of a setup file, and so I think I am already following the advice from that answer.Usually
M
0

I'm currently migrating a project from Jest to Vitest and ran into a similar issue. I'm also using React Query + MSW.

So far what I've observed is that the error seems to occur when a test finishes before the query has completed.

For example, here's a test where I see the issue:

describe('Page', () => {
  it('should render the page', () => {
    renderWithProviders(<Page />)

    expect(screen.getByTestId('heading')).toHaveTextContent('Page')
  })
})

The Page component performs a query which is mocked by msw.

The test will pass but then a connection error is thrown:

Error: connect ECONNREFUSED ::1:3000

However, if I update the test to wait for the query to resolve before the expect statement, then the test completes successfully and no error is thrown.

Moria answered 4/12, 2023 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.