How to test snapshots with Jest and new React lazy 16.6 API

I had a similar problem where I wanted to do snapshot testing of nested components and one of them where lazy loaded. The nesting looked something like this:

SalesContainer -> SalesAreaCard -> SalesCard -> AreaMap

Where SalesContainer is the top component. The AreaMap-component is lazy loaded by SalesCard using React lazy and Suspense. The tests passed locally with AreaMap rendered in the snapshot for most developers. But the tests always failed miserably in Jenkins CI with the AreaMap never rendered. Flaky to say the least.

To make the tests pass I added the magic line await testRenderer.getInstance().loadingPromise; to the tests. This is an example of a test:

import React from 'react';
import renderer from 'react-test-renderer';
import wait from 'waait';
import SalesContainer from './index';

describe('<SalesContainer />', () => {
it('should render correctly', async () => {
    const testRenderer = renderer.create(
      <SalesContainer />
    );
    await wait(0);
    await testRenderer.getInstance().loadingPromise;
    expect(testRenderer).toMatchSnapshot();
  });
});

With Enzyme and mount this worked for me. It does not require changing any exports.

// wait for lazy components
await import('./Component1')
await import('./Component2')

jest.runOnlyPendingTimers()
wrapper.update()

Thanks to Andrew Ferk's comment on the accepted answer.


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(); 
  });
});

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();
  })
})