Testing async http code (axios or fetch) using enzyme vs react testing library

Just as we mentioned in previous posts, the key difference between enzyme and react testing library is that react testing library does not provide methods to access the state or props of the component. It encourages you to write the test cases which test the scenario the way the user uses it. So you are testing the functionality of the component and not the implementation. This also helps in keeping the tests non-brittle.

When it comes to testing the asynchronous code like HTTP functions, the way we test is similar is enzyme and react testing library. In both the scenarios, we have to mock the http call and return the response that we want.

Lets take an example of component which makes an HTTP call and renders the data in list. The component uses axios libary to make the http calls.

import React, { Component } from "react";
import { map } from "lodash";
import axios from "axios";

class AsyncTests extends Component {
  state = {
    data: []
  };

  axiosFn() {
    axios({
      url: "http://google.com/somedata.json"
    })
      .then(response => {
        this.setState({
          data: response.data
        });
      })
      .catch(e => {
        console.log("error", e);
      });
  }

  render() {
    const { data } = this.state;
    return (
      <div classname="App">
        <List data="{data}">
        <button id="async" onclick="{this.axiosFn}">
          Async Function
        </button>
      </List></div>
    );
  }
}

export const List = props => {
  const { data } = props;
  if(data.length === 0){
    return <div>Loading</div>
  }
  return map(data, d => <span classname="index2">{d}</span>);
};

export default AsyncTests;

What functionality are we going to test

Functionality of the above component - When the user clicks on the button, it should render the data from the URL in list form.

Unit testing using enzyme

We are going to mock the api response using a library called axios-mock-adapter. Lets see the code for the unit test case in enzyme. We have following tests in enzyme.

  • Testing the success condition in enzyme. We check the response from the url sets the state of the component.
  • Testing the list render. We check that the state is rendered to list.

We can write more tests like handling the error condition etc. You can read more about this in the article.

it("should handle axios function - success", async done => {
  const mock = new MockAdapter(axios);
  mock.onGet(/.*/g).reply(200, {
    name: "abc"
  });
  const app = shallow(<AsyncTests>);
  app.setState({
    data: []
  });
  app.instance().axiosFn();
  await waitUntil(() => {
    return !_.isEmpty(app.state("data"));
  });
  expect(app.state("data")).toEqual({ name: "abc" });
  done();
});

it("should render the list", async done => {
  const mock = new MockAdapter(axios);
  mock.onGet(/.*/g).reply(200, [1, 2, 3]);
  const app = shallow(<AsyncTests>);
  app.setState({
    data: []
  });
  app.instance().axiosFn();
  await waitUntil(() => {
    return !_.isEmpty(app.state("data"));
  });
  expect(app.state("data")).toEqual([1, 2, 3]);
  app.instance().forceUpdate();
  expect(app.find(List)).toHaveLength(1);
  done();
});

Unit testing using react testing library

In react testing library, we too mock the url. Let us see the test below.

describe("Aync Tests", () => {
  it("should render the list", async (done) => {
    const mock = new MockAdapter(axios);
    mock.onGet(/.*/g).reply(200, {
      data: ["1", "2"]
    });
    const {container} = render(<AsyncTests/>);
    fireEvent.click(container.querySelector("#async"), new MouseEvent('click', {
      bubbles: true,
      cancelable: true,
    }));
    const indexs = Array.from(document.querySelectorAll(".index2")).map(a => a.firstChild.textContent)
    expect(indexs).toEqual(["1", "2"]);
    done();
  });
});

We learnt how to unit test the axios using MockAdapter. It is possible to mock using jest too, without the use of MockAdapter. Please go through the tutorial to learn how to unit test axios or fetch calls in jest without use of MockAdapter.