Jest -- Mock a function called inside a React Component

31,433

Solution 1

You have to mock the ./api module like this and import it so you can set the implemenation of the mock

import { apiGetMethod } from './api'

jest.mock('./api', () => ({ apiGetMethod: jest.fn() }))

in your test can set how the mock should work using mockImplementation:

apiGetMethod.mockImplementation(() => Promise.resolve('test1234'))

Solution 2

In case the jest.mock method from @Andreas's answer did not work for you. you could try the following in your test file.

const api = require('./api');
api.apiGetMethod = jest.fn(/* Add custom implementation here.*/);

This should execute your mocked version of the apiGetMethod inside you Foo component.

Solution 3

Here is an updated solution for anyone struggling with this in '21. This solution uses Typescript, so be aware of that. For regular JS just take out the type calls wherever you see them.

You import the function inside your test at the top

import functionToMock from '../api'

Then you indeed mock the call to the folder outside of the tests, to indicate that anything being called from this folder should and will be mocked

[imports are up here]

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

[tests are down here]

Next we mock the actual function we're importing. Personally I did this inside the test, but I assume it works just as well outside the test or inside a beforeEach

(functionToMock as jest.Mock).mockResolvedValue(data_that_is_returned);

Now here's the kicker and where everyone seems to get stuck. So far this is correct, but we are missing one important bit when mocking functions inside a component: act. You can read more on it here but essentially we want to wrap our render inside this act. React testing library has it's own version of act. It is also asynchronous, so you have to make sure your test is async and also define the destructured variables from render outside of it.

In the end your test file should look something like this:

import { render, act } from '@testing-library/react';
import UserGrid from '../components/Users/UserGrid';
import { data2 } from '../__fixtures__/data';
import functionToMock from '../api';

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

describe("Test Suite", () => {
  it('Renders', async () => {
    (functionToMock as jest.Mock).mockResolvedValue(data2);

    let getAllByTestId: any;
    let getByTestId: any;
    await act(async () => {
      ({ getByTestId, getAllByTestId } = render(<UserGrid />));
    });
    const container = getByTestId('grid-container');
    const userBoxes = getAllByTestId('user-box');
  });
});

Share:
31,433
Ryan Castner
Author by

Ryan Castner

Software Engineer, JavaScript/React, Python/Django, Christian✝️, Husband💕, Love Books📚, Football🏈, and my two Heckos🐕 Piper &amp; Dolly

Updated on July 20, 2022

Comments

  • Ryan Castner
    Ryan Castner almost 2 years

    Jest provides a way to mock functions as described in their docs

    apiGetMethod = jest.fn().mockImplementation(
        new Promise((resolve, reject) => {
            const userID = parseInt(url.substr('/users/'.length), 10);
            process.nextTick(
                () => users[userID] ? resolve(users[userID]) : reject({
                    error: 'User with ' + userID + ' not found.',
                });
            );
        });
    );
    

    However these mocks only seem to work when the function is called directly in a test.

    describe('example test', () => {
        it('uses the mocked function', () => {
            apiGetMethod().then(...);
        });
    });
    

    If I have a React Component defined as such how can I mock it?

    import { apiGetMethod } from './api';
    
    class Foo extends React.Component {
        state = {
            data: []
        }
    
        makeRequest = () => {
           apiGetMethod().then(result => {
               this.setState({data: result});
           });
        };
    
        componentDidMount() {
            this.makeRequest();
        }
    
        render() {
            return (
               <ul>
                 { this.state.data.map((data) => <li>{data}</li>) }
               </ul>
            )   
        }
    }
    

    I have no idea how to make it so Foo component calls my mocked apiGetMethod() implementation so that I can test that it renders properly with data.

    (this is a simplified, contrived example for the sake of understanding how to mock functions called inside react components)

    edit: api.js file for clarity

    // api.js
    import 'whatwg-fetch';
    
    export function apiGetMethod() {
       return fetch(url, {...});
    }