how to debug a test timing out waiting for React.lazy import
Asked Answered
L

2

6

I have a React+vite app for which I'm writing test to cover the front-end routing redirection logic at application startup.

Routing is handled by react-router v6, and all components associated with routes are wrapped in React.lazy. Tests are ran by vitest and I'm using react-testing-library helpers

All the tests are similar and look like this

it('Redirects from app root to red room if the user has a red shirt', async () => {
    getUser.mockReturnValue(redShirtUser);
    render(MyTestedComponent, { wrapper });

    await waitFor(() => expect(screen.getByText('Welcome to the red room'));
    expect(history.location.pathname).toBe('/red-room');
  });

One of the tests, though, is taking significantly longer than the others, to the point that waitFor times out. I can specify a longer timeout to waitFor, but it will still not reliably run on the CI. This happens also if the test is the only one in its file/the only one being executed.

I have narrowed down the slow part (through the magic of console.log debugging) to be the lazy import() statement - it takes a lot (seconds) until the module is imported and executed.

How can I debug this? Are there things known to cause (lazy) imports to become slow?

Lysippus answered 24/8, 2022 at 14:46 Comment(3)
Lazy imports (in node.js) can be very slow if you have a lot of code/dependencies. You can remove this time from being counted as a part of the test by preloading it in the setup phase - just put await import('./path'). I've never used vitest, so idk how it's configured, but I assume it should have a setup phase.Vashtivashtia
@Vashtivashtia thanks for the suggestion! it did not help me understand why, but it's an excellent workaround! if you want to expand on it and post this as an answer I'll be happy to accept itLysippus
I tried explaining why it happens, as well as providing more suggestions. I hope it helps!Vashtivashtia
V
8

Node's module resolution is very slow. So if your script imports/requires a lot of other scripts/dependencies then your startup time is going to be very slow.

If you're doing lazy imports inside of your tests then this startup time will be counted as a part of the test.

You can avoid that by preloading the scripts, either at the top of your test file or in the setup phase.

Preloading you script at the top of the file is the simplest way,
just put import './path'; or require('./path');

Preloading in the startup phase is only useful if you often run some select tests instead of all test, since you don't want to preload scripts that aren't going to be tested.

In jest this is done using beforeAll() like so:

describe('my thing', () =>
{
    beforeAll(() => import('./path'))

    // alternatively you can use require:
    beforeAll(() => require('./path'))

    // ...your tests...
})

I've never used vitest, so I don't know how setup phase is configured there. I've only found this, but that looks like a global config and it's probably better to put the import at the top of your test script then to do it in a global config.


Side Notes:

Having a component that takes several seconds to load might indicate that it's importing stuff it doesn't need.
That often happens when you have a lot of components in the same file or when you don't move shared code into separate files.
Having a lot of unrelated code in 1 file is a problem for Node because it can't do any tree shaking and it wont skip unused imports.

Also if your components doesn't always render all of it's sub-components then you can use lazy import for those sub-components as well.

Vashtivashtia answered 29/9, 2022 at 11:53 Comment(1)
thanks for the detailed answer. Preloading the import is a great workaround, and I'll try to identify exactly what it's slowing down - we have one component per file, no unused imports, and avoid unrelated code in the same file, so no obvious explanation and more investigation is neededLysippus
S
0

Version before 10 we can use waitForElement, it will take less time.For more details or detailed explanation watch this https://www.youtube.com/watch?v=lfb5jvHq9c4 video. After version 10 whatever you used waitFor is correct.Thanks for correcting @agos.Editing comment to save other users from trouble. But along with that Making sure to use proper error handling in that component using try catch is equally important.

Stob answered 31/8, 2022 at 18:27 Comment(1)
wait and waitForElement have been deprecated since react-testing-library version 10, and removed in version 11. They have been replaced with waitFor, which is what I'm using.Lysippus

© 2022 - 2024 — McMap. All rights reserved.