简体   繁体   中英

Jest component with manual mocked HOC (with CRA)

I want to test a React component wrapped in a HOC (the one I want to mock).

The component to simulate has the following structure

import { myHOC } from '../hoc/Component';

const Component = () => {
  ...
  return (
    ... some JSX
  );
};

export default myHOC(Component);

The HOC has this structure:

export const myHOC = (Component) => {
  return () => {
    ...
    return <Component {...} />
  }
};

And only want to test the Component and mock the HOC, a simplified version of the test is:

import * as HOC from '../hoc/Component';
import Component from './Component';
import '@testing-library/jest-dom';

jest.mock('../hoc/Component', () => ({myHOC: (C) => () => <C /> }));

describe('Testing the Component', () => {
  it('renders the component', async () => {
    HOC.myHOC.mockImplementation((Comp) => () => <Comp {...someTestSpecificProps} />);

    const screen = render(<Component />);
  });
});

However, when running the test, I get the following error:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Pointing at render(<Component />) . Seems like is just taking the jest.mock function, not the mockImplementation .

I know that jest.mock('../hoc/Component', () => ({myHoc: (C) => () => <C/> })); works but I want to pass different props to the Component in each test.


Seems like jest isn't mocking the HOC when the implementation is inside the test. Is there any way to achieve this?

The issue was not the HOC per se, but how the modules are imported and when the code is executed.

As soon as the Component is imported (ie in the import Component from './Component'; ), the HOC is executed and the real import is the closure of the HOC. So just the initial mock has an effect in the code.

Now the way to solve it is to use a dynamic import after the mockImplementation like this:

describe('Testing the Component', () => {
  it('renders the component', async () => {
    HOC.myHOC.mockImplementation((Comp) => () => <Comp {...someTestSpecificProps} />);

    // This is the dynamic import
    const Component = (await import('./Component')).default;

    const screen = render(<Component />);
  });
});

This way at the time the Component is imported, it will retrieve the mock implementation of the test and not the default one.

I forgot to mention that given that imported modules are cached, the result in each import(...) is the same (ie the file is not executed at each import), So jest.isolateModules should be used:

jest.isolateModules(() => {
  const Component = require(path).default;
  const screen = render(<Component />); 
  ... 
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM