How to implement Jest test for Axios and Hooks - useEffect

9.7k views Asked by At

How can I write a Jest test for an Axios call inside an useEffect in React?

I found how to mock the Axios here - https://stackoverflow.com/a/51654713/7883067

I also know how to trigger the useEffect if it is called by user interaction such as the click of a button. But on the following case, the useEffect is being called by an async function that is triggered by the response of Axios.

Example:

Component

import React, { useState, useEffect } from "react";
import axios from "axios";

const LoadImage = () => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);

  const fetchData = async () => {
    return await axios
      .get("https://picsum.photos/200/300.jpg", {
        "Content-Type": "application/xml; charset=utf-8",
      })
      .then((response) => {
        setData(response.config.url);
        setLoading(false);
      })
      .catch((error) => {
        console.log(error);
      });
  };

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

  return (
    <div>
      {loading ? (
        <div>...loading</div>
      ) : (
        <div>
          {console.log(data)}
          <h3>Get Image</h3>
          <img alt="picsum-api" src={`${data}`} />
        </div>
      )}
    </div>
  );
};

export default LoadImage;

Test

// setUpTest.js
// import "@testing-library/jest-dom/extend-expect";
// import { configure } from "enzyme";
// import Adapter from "enzyme-adapter-react-16";
// configure({ adapter: new Adapter() });

import React from "react";
import { shallow } from "enzyme";
import * as axios from "axios";
import LoadImage from "../components/LoadImage";

describe("LoadImage test", () => {
  jest.mock("axios");
  axios.get = jest.fn();

  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<LoadImage />);
  });

  test("good response", () => {
    axios.get.mockImplementation(() => Promise.resolve({ status: 200 }));
    // expect();
  });
});

Could anyone help me to show what I could be missing or doing wrong, please?

1

There are 1 answers

2
Mark Nunes On BEST ANSWER

Following Estus Flask's advice, I managed to test that code by using mount instead of shallow and reading Robin Wieruch's post I understood how to mock Axios promises.

Here is how the code looks now:

Component

import React, { useState, useEffect } from "react";
import axios from "axios";

const LoadImage = () => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);

  const fetchData = async () => {
    return await axios.get("https://picsum.photos/200/300.jpg");
  };

  useEffect(() => {
    fetchData().then((res) => {
      setData(res.config.url);
      setLoading(false);
    });
  }, []);

  return (
    <div>
      {loading ? (
        <div>...loading</div>
      ) : (
        <div>
          <h3>Get Image</h3>
          <img alt="picsum-api" src={`${data}`} />
        </div>
      )}
    </div>
  );
};

export default LoadImage;

Test

import React from "react";
import { mount, shallow } from "enzyme";
import axios from "axios";
import { act } from "react-dom/test-utils";

import LoadImage from "../components/LoadImage";

jest.mock("axios");

// mock data
const data = {
  config: {
    url: "image-url",
  },
};

describe("LoadImage test", () => {
  let wrapper;

  // clear all mocks
  afterEach(() => {
    jest.clearAllMocks();
  });

  test("renders with loading", () => {
    wrapper = shallow(<LoadImage />);    
    expect(wrapper.find("div").first().text()).toBe("...loading");
  });

  test("loads img", async () => {

    // mock axios promise
    await act(async () => {
      await axios.get.mockImplementationOnce(() => Promise.resolve(data));
      wrapper = mount(<LoadImage />);
    });

    // check the render output
    wrapper.update();

    await expect(axios.get).toHaveBeenCalledWith("https://picsum.photos/200/300.jpg");

    await expect(axios.get).toHaveBeenCalledTimes(1);

    await expect(wrapper.find("img").props().src).toEqual("image-url");
  });
});