Cómo implementar la prueba Jest para Axios y Hooks - useEffect

Sep 28 2020

¿Cómo puedo escribir una prueba de Jest para una llamada de Axios dentro de un useEffect en React?

Encontré cómo burlarme de los Axios aquí: https://stackoverflow.com/a/51654713/7883067

También sé cómo activar useEffect si lo llama la interacción del usuario, como el clic de un botón. Pero en el siguiente caso, useEffect está siendo llamado por una función asíncrona que es activada por la respuesta de Axios.

Ejemplo:

Componente

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;

Prueba

// 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();
  });
});

¿Alguien podría ayudarme a mostrar lo que podría estar perdiendo o haciendo mal, por favor?

Respuestas

4 MarkNunes Sep 30 2020 at 23:26

Siguiendo el consejo de Estus Flask, logré probar ese código usando mount en lugar de superficial y leyendo la publicación de Robin Wieruch entendí cómo burlarme de las promesas de Axios.

Así es como se ve el código ahora:

Componente

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;

Prueba

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