admin管理员组

文章数量:1392073

I'm trying to write a Jest unit test for a ponent that uses React.Suspense.

Simplified versions of my ponent modules under test:

MyComponent.js

import React from 'react';

export default () => <h1>Tadaa!!!</h1>;

MySuspendedComponent.js

import React, { Suspense } from 'react';
import MyComponent from './MyComponent';

export default () => (
    <Suspense fallback={<div>Loading…</div>}>
        <MyComponent />
    </Suspense>
);

Naïvely, in my first attempt, I wrote a unit test that uses Enzyme to mount the suspended ponent:

MySuspendedComponent.test.js

import React from 'react';
import { mount } from 'enzyme';
import MySuspendedComponent from './MySuspendedComponent';

test('the suspended ponent renders correctly', () => {
    const wrapper = mount(<MySuspendedComponent />);
    expect(wrapper.html()).toMatchSnapshot();
});

This causes the test to crash with the error message:

Error: Enzyme Internal Error: unknown node with tag 13

Searching for the error message on the web, I found that this is most likely caused by Enzyme not being ready to render Suspense (yet).

If I use shallow instead of mount, the error message changes to:

Invariant Violation: ReactDOMServer does not yet support Suspense

My next attempt was to mock out Suspense with a dummy pass-through ponent, like this:

MySuspendedComponent.test.js

import React from 'react';
import { mount } from 'enzyme';
import MySuspendedComponent from './MySuspendedComponent';

jest.mock('react', () => {
    const react = require.requireActual('react');
    return () => ({
        ...react,
        Suspense({ children }) {
            return children;
        }
    });
});

test('the suspended ponent renders correctly', () => {
    const wrapper = mount(<MySuspendedComponent />);
    expect(wrapper.html()).toMatchSnapshot();
});

The idea is to have a mock implementation of the React module that contains all the actual code from the React library, with only Suspense being replaced by a mock function.

I've used this pattern with requireActual, as described in the Jest documentation, successfully in other unit tests when mocking other modules than React, but with React, it does not work.

The error I get now is:

TypeError: (($_$w(...) , react) || ($$w(...) , _load_react(...))).default.createElement is not a function

…which, I assume, is caused by the original implementation of React not being available after my mocking trick.

How can I mock out Suspense while leaving the rest of the React library intact?

Or is there another, better way to test suspended ponents?

I'm trying to write a Jest unit test for a ponent that uses React.Suspense.

Simplified versions of my ponent modules under test:

MyComponent.js

import React from 'react';

export default () => <h1>Tadaa!!!</h1>;

MySuspendedComponent.js

import React, { Suspense } from 'react';
import MyComponent from './MyComponent';

export default () => (
    <Suspense fallback={<div>Loading…</div>}>
        <MyComponent />
    </Suspense>
);

Naïvely, in my first attempt, I wrote a unit test that uses Enzyme to mount the suspended ponent:

MySuspendedComponent.test.js

import React from 'react';
import { mount } from 'enzyme';
import MySuspendedComponent from './MySuspendedComponent';

test('the suspended ponent renders correctly', () => {
    const wrapper = mount(<MySuspendedComponent />);
    expect(wrapper.html()).toMatchSnapshot();
});

This causes the test to crash with the error message:

Error: Enzyme Internal Error: unknown node with tag 13

Searching for the error message on the web, I found that this is most likely caused by Enzyme not being ready to render Suspense (yet).

If I use shallow instead of mount, the error message changes to:

Invariant Violation: ReactDOMServer does not yet support Suspense

My next attempt was to mock out Suspense with a dummy pass-through ponent, like this:

MySuspendedComponent.test.js

import React from 'react';
import { mount } from 'enzyme';
import MySuspendedComponent from './MySuspendedComponent';

jest.mock('react', () => {
    const react = require.requireActual('react');
    return () => ({
        ...react,
        Suspense({ children }) {
            return children;
        }
    });
});

test('the suspended ponent renders correctly', () => {
    const wrapper = mount(<MySuspendedComponent />);
    expect(wrapper.html()).toMatchSnapshot();
});

The idea is to have a mock implementation of the React module that contains all the actual code from the React library, with only Suspense being replaced by a mock function.

I've used this pattern with requireActual, as described in the Jest documentation, successfully in other unit tests when mocking other modules than React, but with React, it does not work.

The error I get now is:

TypeError: (($_$w(...) , react) || ($$w(...) , _load_react(...))).default.createElement is not a function

…which, I assume, is caused by the original implementation of React not being available after my mocking trick.

How can I mock out Suspense while leaving the rest of the React library intact?

Or is there another, better way to test suspended ponents?

Share Improve this question edited Jan 10, 2019 at 16:22 skyboyer 23.8k7 gold badges62 silver badges71 bronze badges asked Jan 2, 2019 at 17:10 Patrick HundPatrick Hund 20.3k12 gold badges71 silver badges95 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

The solution is not to use object spreading to export the original React module, but simply overwriting the Suspense property, like this:

MySuspendedComponent.test.js

import React from 'react';
import { mount } from 'enzyme';
import MySuspendedComponent from './MySuspendedComponent';

jest.mock('react', () => {
    const React = jest.requireActual('react');
    React.Suspense = ({ children }) => children;
    return React;
});

test('the suspended ponent renders correctly', () => {
    const wrapper = mount(<MySuspendedComponent />);
    expect(wrapper.html()).toMatchSnapshot();
});

This creates the following snapshot, as expected:

MySuspendedComponent.test.js.snap

exports[`the suspended ponent renders correctly 1`] = `"<h1>Tadaa!!!</h1>"`;

I needed to test my lazy ponent using Enzyme. Following approach worked for me to test on ponent loading pletion:

const myComponent = React.lazy(() => 
      import('@material-ui/icons')
      .then(module => ({ 
         default: module.KeyboardArrowRight 
      })
   )
);

Test Code ->

    //mock actual ponent inside suspense
    jest.mock("@material-ui/icons", () => { 
        return {
            KeyboardArrowRight: () => "KeyboardArrowRight",
    }
    });
    
    const lazyComponent = mount(<Suspense fallback={<div>Loading...</div>}>
               {<myComponent>}
           </Suspense>);
        
    const ponentToTestLoaded  = await ponentToTest.type._result; // to get actual ponent in suspense
        
    expect(ponentToTestLoaded.text())`.toEqual("KeyboardArrowRight");

This is hacky but working well for Enzyme library.

本文标签: javascriptMocking out ReactSuspense Whilst Leaving the Rest of React IntactStack Overflow