Using Jest to mock AJAX requests for React

Viewed 1309

I’m building a React component which, when mounted, gets data from the server via AJAX.

I’m trying (and failing) to mock the AJAX call in Jest and am wondering if I’m just misunderstanding how mocking in Jest really works?

My file structure is:

  • index.js
  • index.test.js
  • requests.js
  • __mocks__
    • requests.js

The component (index.js) makes the call via requests.js, which uses Axios to get data from the server. requests.js is mocked inside the __mocks__ folder.

As can be seen below in the test file index.test.js, I’m using getSpy to detect if the requests.getData function is called. It never is. I should add that I’ve also tried to get the component to simply render some of the data from the getData function response, and then, in the test file, run console.log(ListWrapper.debug()); to see whether the data is rendered in the test. Again, it never is.

Just wondering if anyone can spot where I’m going wrong, or if I’m just getting the wrong end of the stick?

index.test.js

import React from 'react';
import { mount } from 'enzyme';
import List from './index';
import expect from 'expect';

import requests from './requests';

jest.mock('requests');

describe('Renders list', () => {
    const getSpy = jest.spyOn(requests, 'getData');

    const ListWrapper = mount(
        <ListWrapper />
    );

    console.log(ListWrapper.debug());

    it('should fetch currencies', () => {
        expect(getSpy).toBeCalled();   
    });
});

__mocks__/requests.js

module.exports = {
    getData: () => {
        return new Promise((resolve) => {
            resolve({
               [
                    {
                        id: 1,
                        name: 'Bob Smith',
                        age: 32
                    }
                ]
            });
        });

    }
};

index.js (shortened)

/**
 * List component
 */
import React, { useState, useEffect, useCallback } from 'react';
import * as requests from './requests’;
function List(props) {

  const [data, setData] = useState([]);

   /**
     * Get data from server
     */
   const getCurrencies = useCallback(() => {
      r4_uiTools.loadingAnimation.show();
      requests
          .getData()
          .then((response) => {
              setData(response);
          })
          .catch(error) {
              //...
          }

   /**
     * Get data when first mounting component
     */
    useEffect(() => {
        getData();
    }, [getData]);

  return(
   <>
    /* ... */
    {data[0].name}
   </>
  );

requests.js (shortened)

import axios from 'axios';

const getData = () => {
    return new Promise((resolve, reject) => {
        const url = `/MyURL/`;
        axios
            .get(url)
            .then((response) => {
                resolve(response);
            })
            .catch((error) => {
                reject(error);
            });
    });
};

export { getData };
4 Answers

Shouldn't your useEffect look like this?

useEffect(() => {
    getCurrencies();
}, []);

Also, what is ListWrapper in your index.test.js? Is it a typo and should be List?

If you are mocking the call and returning the Promise you will have to make your it block async

Also please put the mount inside the it block otherwise you have to make your describe async

describe('Renders list', () => {
    ...
    ...
    it('should fetch currencies', async () => {
        expect(getSpy).toBeCalled();   
    });
});

Your jest.mock for request seems incorrect. If its not a node_module, it should point to the path just like your import: jest.mock('./requests');

You also won't need a spy now since jest.mock mocks all the exports by './request`. If it was me, I would write the test like this:

import React from 'react';
import { mount } from 'enzyme';
import List from './index';
import expect from 'expect';

import requests from './requests';

jest.mock('./requests', () => {    // Path instead of name when mocking own modules
    return {
        default: {
            getData: jest.fn().mockResolvedValue('some value')    // explicit mock
        },
        // ... any other exports you'd wanna mock 
    };
});

describe('Renders list', () => {

    const ListWrapper = mount(
        <ListWrapper />
    );

    console.log(ListWrapper.debug());

    it('should fetch currencies', () => {
        expect(requests.getData).toBeCalled();  // Directly check on getData since it should point to a mock function   
    });
});

If you should choose spyOn or jest.mock to do that. With jest.mock, it should be :

jest.mock('./requests', () => ({ 
        getData: jest.fn(),
    }));

And if you want to handle response data when call this. You must to use wait from @testing-library/react. You can read more at here

Related