Come implementare il test Jest per Axios e Hooks - useEffect

Sep 28 2020

Come posso scrivere un test Jest per una chiamata Axios all'interno di un useEffect in React?

Ho scoperto come deridere gli Axios qui - https://stackoverflow.com/a/51654713/7883067

So anche come attivare useEffect se viene richiamato dall'interazione dell'utente come il clic di un pulsante. Ma nel caso seguente, useEffect viene chiamato da una funzione asincrona che viene attivata dalla risposta di Axios.

Esempio:

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;

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

Qualcuno potrebbe aiutarmi a mostrare cosa potrei perdere o fare di sbagliato, per favore?

Risposte

4 MarkNunes Sep 30 2020 at 23:26

Seguendo il consiglio di Estus Flask, sono riuscito a testare quel codice usando mount invece di superficiale e leggendo il post di Robin Wieruch ho capito come deridere le promesse di Axios.

Ecco come appare il codice ora:

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;

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