问题
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