Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest -- Mock a function called inside a React Component

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, {...});
}
like image 664
Ryan Castner Avatar asked Apr 19 '17 15:04

Ryan Castner


People also ask

How do you mock a function inside a React component?

Default Export To mock a React component, the most straightforward approach is to use the jest. mock function. You mock the file that exports the component and replace it with a custom implementation. Since a component is basically a function, the mock should also return a function.

How do you mock the return value of a function in Jest?

To mock a function's return value in Jest, you first need to import all named exports from a module, then use mockReturnValue on the imported function. You can use the * as <alias> inside an import statement to import all named exports. Then, you need to chain mockReturnValue off of jest.

How do you check if a method is called in Jest?

To check if a component's method is called, we can use the jest. spyOn method to check if it's called. We check if the onclick method is called if we get the p element and call it.


3 Answers

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'))
like image 151
Andreas Köberle Avatar answered Oct 10 '22 10:10

Andreas Köberle


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.

like image 25
Nahush Farkande Avatar answered Oct 10 '22 09:10

Nahush Farkande


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');
  });
});

like image 3
Valentin Avatar answered Oct 10 '22 09:10

Valentin