How to replace a React component with a mock when testing with Jest
Asked Answered
Y

1

8

I am really stuck on this in the project I am working on, and all the answers I have found seem to be so simple but they haven't worked for me. Perhaps I don't really understand what a mock is, and I could really use some guidance.

I am testing a parent component that has a child component that fetches some data from a database using GraphQL. While testing the parent, I do not care what the child is doing. I want to replace the child with a mocked component (one that doesn't fetch data from a database) so that I can only focus on the parent.

I have come up with the simplest example possible to show my situation. Note that I'm using React Native and React Native Testing Library.

./src/ParentComponent.js

import React from 'react';
import { Text, View } from 'react-native';
import ChildComponent from './ChildComponent';

const ParentComponent = () => (
  <View>
    <Text>Hello World</Text>
    <ChildComponent />
  </View>
);

export default ParentComponent;

./src/ChildComponent.js

import React from 'react';
import { useQuery } from 'react-apollo';
import { View, Text } from 'react-native';
import gql from 'graphql-tag';

const CURRENT_USER_QUERY = gql`
  query {
    currentUser {
      username
    }
  }
`;

const ChildComponent = () => {
  const { data } = useQuery(CURRENT_USER_QUERY);
  const { username } = data.currentUser;

  return (
    <View>
      <Text>Welcome, {username}</Text>
    </View>
  );
};

export default ChildComponent;

./src/__mocks__/ChildComponent.js

import React from 'react';
import { Text } from 'react-native';

const ChildComponent = () => <Text>Welcome.</Text>;

export default ChildComponent;

./src/ParentComponent.test.js

import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { render } from '@testing-library/react-native';
import ParentComponent from '../ParentComponent';

it(`should render the parent component.`, () => {
  jest.mock('../ChildComponent');
  const { getByText } = render(
    <MockedProvider>
      <ParentComponent />
    </MockedProvider>
  );

  expect(getByText('Hello World')).toBeTruthy();
});

When I run the test, I get the following error...

  ● should render the parent component.

    TypeError: Cannot read property 'currentUser' of undefined

      14 | const ChildComponent = () => {
      15 |   const { data } = useQuery(CURRENT_USER_QUERY);
    > 16 |   const { username } = data.currentUser;
         |                             ^
      17 |
      18 |   return (
      19 |     <View>

      at ChildComponent (src/ChildComponent.js:16:29)

It is still using the real <ChildComponent />. Why is it not replacing the <ChildComponent /> with the mocked version in the __mocks__ directory? Is that not how mocks work? If someone can please help and explain, it would be much appreciated. Thank you.

Yevette answered 18/6, 2020 at 17:1 Comment(2)
You are mocking the wrong file: it should be jest.mock(“path-to-mock/__mock__/ChildComponent.js”)Debus
Thank you for replying. I tried it. That did not work either, and according to the jest docs, having the file in that directory, should pick the file automatically... jestjs.io/docs/en/manual-mocks.htmlWizen
Y
5

After days of wracking my brain on this, I finally figured out what was wrong. I was calling the mock inside of the it declaration. When I moved the line, jest.mock('../ChildComponent');, to the top, everything worked. So, the test file should look like this...

import React from 'react';
import { MockedProvider } from '@apollo/react-testing';
import { render } from '@testing-library/react-native';
import ParentComponent from '../ParentComponent';

jest.mock('../ChildComponent');

it(`should render the parent component.`, () => {
  const { getByText } = render(
    <MockedProvider>
      <ParentComponent />
    </MockedProvider>
  );

  expect(getByText('Hello World')).toBeTruthy();
});
Yevette answered 19/6, 2020 at 20:17 Comment(2)
I was just coming here to say this, congrats on figuring out your issue. For reference, the jest docs: jestjs.io/docs/en/manual-mocks "Note: In order to mock properly, Jest needs jest.mock('moduleName') to be in the same scope as the require/import statement."Catchup
Thank you, Eric. Sometimes it just takes writing up an issue here on SO for the problem to reveal itself. I see that now in the docs. Thanks.Wizen

© 2022 - 2024 — McMap. All rights reserved.