Using Jest to mock a React component with props
Asked Answered
D

7

54

I have a React component which contains some other components that depend on access to a Redux store etc., which cause issues when doing a full Enzyme mount. Let's say a structure like this:

import ComponentToMock from './ComponentToMock';

<ComponentToTest>
  ...some stuff
  <ComponentToMock testProp="This throws a warning" />
</ComponentToTest>

I want to use Jest's .mock() method to mock out the sub-component, so that it is not a concern for the test.

I'm aware that I can mock out a straight component with something like:

jest.mock('./ComponentToMock', () => 'ComponentToMock');

However, as this component would normally receive props, React gets upset, giving a warning about unknown props (in this case, testProp) being passed to <ComponentToMock />.

I've tried to return a function instead, however you can't return JSX (from what I could tell) in a Jest mock, due to it being hoisted. It throws an error in this case.

So my question is how can I either

a) get ComponentToMock to ignore props passed to it, or

b) return a React component that can be used to mock the child component that I'm not worried about testing.

Or... is there a better way?

Dorr answered 7/6, 2017 at 3:49 Comment(1)
I just had a lot of trouble with this myself, but it was answered here: SO QuestionAntilog
A
54

There's a note at the bottom of the docs for jest.mock() for preventing the hoisting behavior:

Note: When using babel-jest, calls to mock will automatically be hoisted to the top of the code block. Use doMock if you want to explicitly avoid this behavior.

Then you can do as you described: return a function that is a stub of the component you don't need to test.

jest.doMock('./ComponentToMock', () => {
  const ComponentToMock = () => <div />;
  return ComponentToMock;
});

const ComponentToTest = require('./ComponentToTest').default;

It's helpful to name the stub component since it gets rendered in snapshots.

Alumnus answered 20/7, 2017 at 14:25 Comment(5)
This is good to know! The docs mention it a little off-handedly. However, I tried this and it still didn't work out. Because the import of <ComponentToMock /> happens when the <ComponentToTest /> is brought in, unless Jest's mock is hoisted to be first, it happens after the fact and the mock does not work.Dorr
I updated the snippet: you have to do the mocking and then require the component to test.Alumnus
I've managed to get this working in a basic sense. It does require the use of... require, ahem, not import. That may be worth considering for anyone with a similar situation. I've got a more complex setup that seems to not be working with the fix, but that could just be some interfering HOCs. Thanks for your help!Dorr
Same here, this solution does not work out, it throws a Invariant Violation: getNodeFromInstance: Invalid argument. Seems that we should live with the warnings upon test output.Breed
Depending on the way your component is exported/imported you might need to do return {ComponentToMock}Ernieernst
D
34

I've learned a little more since I asked this question. Here's an alternative (better?) way of dealing with mocking components that need to be passed props: using module mock files.

First create a file with the same name as the component to mock in a __mocks__ folder under the component's folder e.g.

.
|- /ComponentToMock.js
└- /__mocks__/ComponentToMock.js <-- create this folder/file!

Note: It seems as of the time of writing, the folder must be called __mocks__ (you will need to create __mocks__ in each folder you need to mock components for. If the underscores upset you, just pretend they aren't there ;) )

Next, in this mock file, you can write the file as you wish, e.g.

// This code would live in ./__mocks__/ComponentToMock.js
import React from 'react';
const ComponentToMock = ({ testProp }) => <div>A mock with '{testProp}' passed!</div>;
export default ComponentToMock;

Then in the test file, change the Jest mock statement to:

jest.mock('./ComponentToMock');

When Jest encounters a .mock() without the second function parameter, it automatically looks for a __mocks__ folder. However, even though the mock statement gets hoisted in the component being tested, this doesn't affect the imports of the mock itself - hence why it's able to import and compile a React component!

This seems to work well for mocked components that need to be passed props, and would otherwise produce prop warnings if a nulled function was returned (but which is perfectly acceptable to continue using if the component does not receive props). I hope this helps some people out there.

Dorr answered 23/10, 2017 at 0:43 Comment(2)
The part about pretending not to see the underscores was very helpful. I will!Ingleside
Just wanna add that jest.mock('./ComponentToMock'); needs to be at the same level as the imports (before describe). Took me some minutes to figure that out, I hope to save someone else's timeQuagga
M
6

Addition to accepted answer, If you are using multiple exports then mocking can be done as follows:

// Component.jsx

export { A, B };
// Component-test.js

jest.mock("../src/Component", () => {
  return {
    A: true,
    B: () => {
      return <></>;
    },
  };
})

Reference Blog: https://thoughtbot.com/blog/mocking-react-components-with-jest

Monaxial answered 13/4, 2021 at 8:45 Comment(0)
A
0

In my case, I was unable to make the mock work.

An it turned out to be case-sensitivity issue:

Module Filename: Autosuggest.js

How i was mocking it: jest.mock('./AutoSuggest', ...); <-- wrong cases

Ahlgren answered 12/8, 2020 at 19:49 Comment(0)
D
0

Jest Recommended and works perfect for me for a default export component

https://jestjs.io/docs/next/tutorial-react

jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent');

Devest answered 17/11, 2022 at 17:46 Comment(0)
H
-1
const mockComponent = ComponentName => ({ children, ...props }) => (
  <ComponentName {...{ '[mockComponent]': true }} {...props}>
    {children}
  </ComponentName>
);

export default mockComponent;
jest.mock('../ComponentToMock', () => {
  const mockComponent = require('./mockComponent').default;

  return mockComponent('ComponentToMock');
});
Hoang answered 27/8, 2021 at 7:47 Comment(0)
G
-1

If someone uses Vitest try this

vi.mock('path-to-component', () => {
  return {
    default: () => <div />, // your mock React component
  };
});

EDIT:

with jest

jest.mock("path-to-component", () => {
  return ({ prop1 }) => <div>{prop1}</div>; // use props as it match with actual component
});
Garcia answered 11/10, 2023 at 13:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.