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;
Functionality of the above component - When the user clicks on the button, it should render the data from the URL in list form.
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.
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();
});
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
.