I have to components imported with the new React lazy
API (16.6).
import React, {PureComponent, lazy} from 'react';
const Component1 = lazy(() => import('./Component1'));
const Component2 = lazy(() => import('./Component2'));
class CustomComponent extends PureComponent {
...
render() {
return (
<div>
<Component1 />
<Component2 />
</div>
);
}
}
In my tests, I'm doing the snapshots of this component. It's a very straightforward test:
import { create } from 'react-test-renderer';
const tree = await create(<CustomComponent />).toJSON();
expect(tree).toMatchSnapshot();
In the logs, the test is failing with this error:
A React component suspended while rendering, but no fallback UI was specified.
Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.
Do I have to wrap in every single test suite with <Suspense>...
?
it('should show the component', async () => {
const component = await create(
<React.Suspense fallback={<div>loading</div>}>
<CustomComponent />
</React.Suspense>
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
};
If I do that, I only see in the snapshot the fallback
component.
+ Array [ + <div> + loading + </div>, + ]
So, which is the best way to do it?
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.
When writing snapshot tests for a React component, you first need to have code in a working state. Then, generate a snapshot of its expected output given certain data. The snapshot tests are committed alongside the component. Jest, a testing framework, will compare the snapshot to the rendered output for the test.
React and React Native components are a good use case for snapshot testing. However, snapshots can capture any serializable value and should be used anytime the goal is testing whether the output is correct.
Do I have to wrap in every single test suite with <Suspense>
?
Yes, the
Suspense
component is neccessary for lazily loading child components, particularly providing a fallback and for reconciliation when the lazy components are available.
Export Component1
and Component2
in CustomComponent
so that they can be imported in tests.
import React, {PureComponent, lazy} from 'react';
export const Component1 = lazy(() => import('./Component1'));
export const Component2 = lazy(() => import('./Component2'));
export default class CustomComponent extends PureComponent {
//...
}
Remember that the lazy loaded components are promise-like. Import them in the test, and wait for them to resolve before doing a check that the snapshot matches.
import { create } from 'react-test-renderer';
import React, {Suspense} from 'react';
import CustomComponent, {Component1, Component2} from './LazyComponent';
describe('CustomComponent', () => {
it('rendered lazily', async()=> {
const root = create(
<Suspense fallback={<div>loading...</div>}>
<CustomComponent/>
</Suspense>
);
await Component1;
await Component2;
expect(root).toMatchSnapshot();
})
})
As per this comment in github, you can mock the lazy components with Jest to return the actual components instead, although you would need to move and export lazy statements to their own files for it to work.
// LazyComponent1.ts
import { lazy } from 'react';
export default lazy(() => import('./Component1'));
// CustomComponent.tsx
import React, { PureComponent } from 'react';
import Component1 from './LazyComponent1';
import Component2 from './LazyComponent2';
class CustomComponent extends PureComponent {
...
render() {
return (
<div>
<Component1 />
<Component2 />
</div>
);
}
}
// CustomComponent.spec.tsx
import React, { Suspense } from 'react';
import { create } from 'react-test-renderer';
import CustomComponent from './CustomComponent';
jest.mock('./LazyComponent1', () => require('./Component1'));
jest.mock('./LazyComponent2', () => require('./Component2'));
describe('CustomComponent', () => {
it('should show the component', () => {
const component = await create(
<Suspense fallback={<div>loading</div>}>
<CustomComponent />
</Suspense>
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
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