Mocking out React.Suspense Whilst Leaving the Rest of React Intact

我的梦境 提交于 2019-12-10 12:02:07

问题


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

Simplified versions of my component 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 component:

MySuspendedComponent.test.js

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

test('the suspended component 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 component, 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 component 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 components?


回答1:


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 component renders correctly', () => {
    const wrapper = mount(<MySuspendedComponent />);
    expect(wrapper.html()).toMatchSnapshot();
});

This creates the following snapshot, as expected:

MySuspendedComponent.test.js.snap

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


来源:https://stackoverflow.com/questions/54010426/mocking-out-react-suspense-whilst-leaving-the-rest-of-react-intact

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!