Home > Enterprise >  Testing react component when state changes with REST API calls
Testing react component when state changes with REST API calls

Time:07-24

I want to test my react component when state is updated based on multiple REST calls response.

This is my code.

setuptests.js

import Enzyme from 'enzyme';
import EnzymeAdapter from '@wojtekmaj/enzyme-adapter-react-17';


Enzyme.configure({ adapter: new EnzymeAdapter() });

apiProvider.js:

const BASE_URL = "https://jsonplaceholder.typicode.com";

const get = (url) => {
  return fetch(`${BASE_URL}/${url}`, {
    method: "GET"
  })
    .then((response) => {
      if (response.ok) {
        return response.json();
      } else {
        throw new Error("Something went wrong");
      }
    })
    .then((data) => {
      return data;
    })
    .catch(() => {
      const response = { ok: false };
      return response;
    });
};

export const apiProvider = { get };

apiService.js:

import { apiProvider } from "./apiProvider";

const getData1 = () => {
  return apiProvider.get("todos/1");
};

const getData2 = () => {
  return apiProvider.get("todos/2");
};

export const apiService = {
  getData1,
  getData2,
};

App.js code:

import React, { useState, useEffect } from "react";
import "./App.css";
import logo from "./logo.svg";
import { apiService } from "./apiService";

function App() {
  const [isLoading, setIsLoading] = useState(true);
  const [data1, setData1] = useState([]);
  const [data2, setData2] = useState([]);
  const [hasData1Loaded, setHasData1Loaded] = useState(false);
  const [hasData2Loaded, setHasData2Loaded] = useState(false);

  useEffect(() => {
    if (hasData1Loaded && hasData2Loaded) {
      setIsLoading(false);
    }
  }, [hasData1Loaded, hasData2Loaded]);

  useEffect(() => {
    apiService.getData1().then((response) => {
      setData1(response);
      setHasData1Loaded(true);
    });
  }, []);

  useEffect(() => {
    apiService.getData2().then((response) => {
      setData2(response);
      setHasData2Loaded(true);
    });
  }, []);

  return (
    <div className="App">
      {isLoading && <div>Loading....</div>}
      {!isLoading && <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>}
    </div>
  );
}

export default App;

App.test.js:

import React from "react";
import { mount } from "enzyme";
import App from "./App";
import { act } from "react-dom/test-utils";

describe("App", () => {
  test("renders without error", () => {
    const wrapper = mount(<App />);
    expect(wrapper.find(".App").exists()).toBe(true);
  });

  test("renders learn react link", () => {
    const wrapper = mount(<App />);
    act(() => {
      new Promise((resolve) => setTimeout(resolve, 0));
    });
    wrapper.update();
    console.log(wrapper.debug());
    expect(wrapper.find(".App-link").text()).toBe("Learn React");
  });
});

CodePudding user response:

Using enzyme for react testing ?

Enzyme is an old testing library for React. True support support is stoped at React v16. Wojciech Maj (creator of enzyme-adapter-react-17 that you are using explain why we should stop using it https://dev.to/wojtekmaj/enzyme-is-dead-now-what-ekl)

How to do async testing with enzyme

With enzyme only

Using setImmediate

import React from "react";
import { mount } from "enzyme";
import App from "./App";

describe("App", () => {
  test("renders learn react link", (done) => {
    const wrapper = mount(<App />);

    setImmediate(() => {
      component.update()
      return expect(wrapper.find(".App-link").text()).toBe("Learn React");
      done()
    });
  });
});

Mixing it with react-testing library

React testing library have a util call waitFor that allow you to wait until expect is match

import React from "react";
import { mount } from "enzyme";
import { waitFor } from '@testing-library/react';
import App from "./App";

describe("App", () => {
  test("renders learn react link", async () => {
    const wrapper = mount(<App />);

    await waitFor(() => {
      wrapper.update()
      return expect(wrapper.find(".App-link").text()).toBe("Learn React");
    })
  });
});

How to do async testing with react-testing-library

If you want to migrate to react testing library, your test will looks like this:

import React from "react";
import { waitFor, render } from '@testing-library/react';
import App from "./App";

describe("App", () => {
  test("renders learn react link", async () => {
    const { getByText } = render(<App />);

    await waitFor(() => expect(getByText("Learn React")).toBeInTheDocument())
  });
});

The main difference here is that you have queries to find your elements

  • Related