Introduction
As a developer, writing unit tests is equally important to develop the component. Because unit test ensures that all functionalities are met, all code meets quality and it will help to improve application maintainability. There are many packages (Jest, Enzyme, React Testing Library, etc) are available for creating a unit test in React application. In this, we are going to explore how to mock API calls and write a unit test for API calls using fetch-mock in React application.
fetch-mock
- fetch-mock is used to mock the API call(http requests) in the React applications.
- It supports most of the JavaScript environments, including Node.js, web workers, service workers, and any browser that either supports fetch natively or that can have a fetch polyfill installed.
- fetch-mock is licensed under the MIT license.
Install fetch-mock using the below command.
npm install fetch-mock
fetchMock.mock() method is used to mocking the API calls.
fetchMock.mock(matcher, response, options);
//matcher - An exact URL or regex to match of the URL. It can be String or Regex or Function or Object
//response - It can be status code, string or object literal. It can be String or Object or Function or Promise or Response
//options - More options to configure matching and responding behavior .Type of the options is Object.
Below are few examples of how to use fetch.mock method to mock the API call.
fetchMock.mock('http://example.com', 200);
fetchMock.mock(/.*\.here.*/, 200);
fetchMock.mock((url, opts) => opts.method === 'post', 200);
fetchMock.mock('begin:http://example', 200);
fetchMock.mock('*', {results: []});
fetchMock.mock('*', {throw: new Error('Bad Data'))});
fetchMock.mock('*', new Promise(res => setTimeout(res, 500, 404)));
fetchMock.mock({
url: 'http://example.com',
headers: {
authorization: 'dummy-token'
}
}, 200);
You can also use shorthands for mock() method such as fetchMock.once() to limit to a single API call, fetchMock.get(), fetchMock.post(), fetchMock.put(), fetchMock.delete(), fetchMock.patch() to limit to a http method.
fetchMock.get('http://example.com', 200);
fetchMock.post('http://example.com', 400);
There are many shorthands methods are available which are ,
- .anyOnce() - Create a route that responds to any single request.
- .getOnce(), .postOnce(), .putOnce(), .deleteOnce(), .headOnce(), .patchOnce() - Create a route that only responds to a single request using a particular http method.
- .getAny(), .postAny(), .putAny(), .deleteAny(), .headAny(), .patchAny() - Create a route that responds to any requests using a particular http method.
- .getAnyOnce(), .postAnyOnce(), .putAnyOnce(), .deleteAnyOnce(), .headAnyOnce(), .patchAnyOnce() - Create a route that responds to any single request using a particular http method.
Why do we need to mock an API call?
If you are working in the UI team then, no need to wait until the full API has been completed. You can start and build the react components-based swagger file(provided by the API team) and mocks the API calls and continue to build the components. Once APIs are fully ready then just remove your mock call and integrate with actual API calls. It will help to avoid dependency and reducing integration issues.
How to mock API calls in the React component?
We can import the fetch-mock in our application as follows,
ES modules
import fetchMock from 'fetch-mock';
Commonjs
const fetchMock = require('fetch-mock');
Consider the below "ChildComponent" React component.
//ChildComponent.jsx
import React from 'react';
import { mockAPI } from "./mockAPI";
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
}
this.onClick = this.onClick.bind(this);
}
componentDidMount() {
mockAPI();
}
onClick() {
fetch("https://api.example.com/items")
.then(res => res.json()
.then((data) => {
this.setState({
data: data
});
})
);
}
render() {
return (
<>
<button id="btnClick" onClick={this.onClick}>Click</button>
{
Object.keys(this.state.data).map((item, index) => {
return (<div id="data" key={index}>{item}</div>);
})
}
</>
)
}
}
export default ChildComponent;
In this component, I am calling "https://api.example.com/items" API on the button click event.
I have mocked the above API in the mockAPI() function and call it in the componentDidMount() method.
//mockAPI.js
import fetchMock from 'fetch-mock';
export function mockAPI(){
fetchMock.mock("https://api.example.com/items", { 'Test': 'Test', 'Test1': 'Test1' });
}
When I clicked the button, the mock data will be assigned to state value instead of real API data.
Once real API is ready, remove mockAPI() from componentDidMount() method. So, automatically real API will call and real data will be bind to the component.
How to write a unit test for API calls using fetch-mock
I have written the below unit test for the above "ChildComponent".
//ChildComponent.test.jsx
import ChildComponent from "./ChildComponent";
import { render, fireEvent, act as reactAct, queryByAttribute } from "@testing-library/react";
import fetchMock from 'fetch-mock';
const getById = queryByAttribute.bind(null, "id");
const mockUrlApi = () => {
let urlDeferred;
const urlPromise = new Promise((resolve, reject) => {
urlDeferred = { reject, resolve };
});
fetchMock.getOnce("https://api.example.com/items", urlPromise, { overwriteRoutes: false });
return urlDeferred;
};
const mockResponse = { "New Mock": "New Mock Data" };
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
test("ChildComponent component render correctly", async () => {
const urlDeferred = mockUrlApi();
let dom;
reactAct(() => {
dom = render(<ChildComponent />);
});
const btn = getById(dom.container, "btnClick");
fireEvent.click(btn);
await reactAct(async () => {
await urlDeferred.resolve(mockResponse);
});
const dataElement = getById(dom.container, "data");
expect(dataElement.length).toBe(1);
dom.unmount();
});
In the above test case,
Mock the API call inside the mockUrlApi() method using fetchMock() function.
fetchMock.getOnce("https://api.example.com/items", urlPromise, { overwriteRoutes: false });
Render the ChildComponent.
let dom;
reactAct(() => {
dom = render(<ChildComponent />);
});
Invoke the button click event using "@testing-library/react".
const btn = getById(dom.container, "btnClick");
fireEvent.click(btn);
Call mock API and pass the mock data to it.
await reactAct(async () => {
await urlDeferred.resolve(mockResponse);
});
Check the mock data is displayed or not (using length of the div element).
const dataElement = getById(dom.container, "data");
expect(dataElement).toHaveLength(1);
Summary
- fetch-mock is used to mock the API call(http requests) in the React applications.
- Mocking the API calls is used to avoid dependency and reducing integration issues.
I hope you have liked it and know about how to mock API calls and write a unit test for API calls using fetch-mock in React application.