Here's my lazy component:
const LazyBones = React.lazy(() => import('@graveyard/Bones')
.then(module => ({default: module.BonesComponent}))
export default LazyBones
I'm importing it like this:
import Bones from './LazyBones'
export default () => (
<Suspense fallback={<p>Loading bones</p>}>
<Bones />
</Suspense>
)
And in my test I have this kind of thing:
import * as LazyBones from './LazyBones';
describe('<BoneYard />', function() {
let Bones;
let wrapper;
beforeEach(function() {
Bones = sinon.stub(LazyBones, 'default');
Bones.returns(() => (<div />));
wrapper = shallow(<BoneYard />);
});
afterEach(function() {
Bones.restore();
});
it('renders bones', function() {
console.log(wrapper)
expect(wrapper.exists(Bones)).to.equal(true);
})
})
What I expect is for the test to pass, and the console.log to print out:
<Suspense fallback={{...}}>
<Bones />
</Suspense>
But instead of <Bones />
I get <lazy />
and it fails the test.
How can I mock out the imported Lazy React component, so that my simplistic test passes?
You can use @testing-library/react to test the React. lazy() method. In addition, if the lazy-loaded component is too complex and you only want to test the parent component that contains the component, you can use the jest. mock() method To provide a mocked version of the subcomponent.
Testing The ParentComponent is Setting Props Correctly render(<ParentComponent open data="some data" />); // Check that the Jest mock function is called with an object. // Use 'expect. objectContaining' to make sure any other default // React props are ignored. expect(mockChildComponent). toHaveBeenCalledWith( expect.
import { lazy, Suspense } from "react"; import { Routes, Route, Outlet, Link } from "react-router-dom"; import HomePage from "./pages/Home"; const Dashboard = lazy(() => import("./pages/Dashboard")); const Notifications = lazy(() => import("./pages/Notifications")); export default function App() { return ( <div ...
Benefits of lazy loading React componentThe major benefit of React lazy load is performance. Loading less JavaScript code to the browser will reduce DOM load time and boost the performance of our application. Users are able to access a web page even if everything has not been loaded.
I'm not sure this is the answer you're looking for, but it sounds like part of the problem is shallow
. According to this thread, shallow
won't work with React.lazy
.
However, mount
also doesn't work when trying to stub a lazy component - if you debug the DOM output (with console.log(wrapper.debug())
) you can see that Bones
is in the DOM, but it's the real (non-stubbed-out) version.
The good news: if you're only trying to check that Bones
exists, you don't have to mock out the component at all! This test passes:
import { Bones } from "./Bones";
import BoneYard from "./app";
describe("<BoneYard />", function() {
it("renders bones", function() {
const wrapper = mount(<BoneYard />);
console.log(wrapper.debug());
expect(wrapper.exists(Bones)).to.equal(true);
wrapper.unmount();
});
});
If you do need to mock the component for a different reason, jest
will let you do that, but it sounds like you're trying to avoid jest
. This thread discusses some other options in the context of jest
(e.g.
mocking Suspense
and lazy
) which may also work with sinon
.
You don't need to resolve lazy()
function by using .then(x => x.default)
React already does that for you.
React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. React code splitting
Syntax should look something like:
const LazyBones = React.lazy(() => import("./LazyBones"))
Example:
// LazyComponent.js
import React from 'react'
export default () => (
<div>
<h1>I'm Lazy</h1>
<p>This component is Lazy</p>
</div>
)
// App.js
import React, { lazy, Suspense } from 'react'
// This will import && resolve LazyComponent.js that located in same path
const LazyComponent = lazy(() => import('./LazyComponent'))
// The lazy component should be rendered inside a Suspense component
function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</Suspense>
</div>
)
}
As for Testing, you can follow the React testing example that shipped by default within create-react-app
and change it a little bit.
Create a new file called LazyComponent.test.js
and add:
// LazyComponent.test.js
import React, { lazy, Suspense } from 'react'
import { render, screen } from '@testing-library/react'
const LazyComponent = lazy(() => import('./LazyComponent'))
test('renders lazy component', async () => {
// Will render the lazy component
render(
<Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</Suspense>
)
// Match text inside it
const textToMatch = await screen.findByText(/I'm Lazy/i)
expect(textToMatch).toBeInTheDocument()
})
Live Example: Click on the Tests Tab just next to Browser tab. if it doesn't work, just reload the page.
You can find more react-testing-library complex examples at their Docs website.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With