Mock Service -React Testing lib — MSW | React unit testing
Simulating API Response in Tests(React)
Problem- How to simulate API response in react unit testing.
Solution- While doing unit testing, we do not call real Api’s but we mock API responses. there are many ways to mock/test API response but one of the most reliable and better way is the use of the MSW(Mock Service Worker) package.
Let's do API testing in React
Step -1
- Create React app using CRA
- Install Axios (npm i axios)
- Install MSW(npm install msw — save-dev)
Step -2 Update setupTests.js as below
import '@testing-library/jest-dom';
import {server} from './mocks/server.js'
// Establish API mocking before all tests.
beforeAll(() => server.listen())
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())
// Clean up after the tests are finished.
afterAll(() => server.close())
Step -3 Create a Page where API will be called(Post page)
import {useEffect, useState} from "react";
import axios from "axios";
import Post from "../../components/post/Post";
const Posts = () => {
useEffect(() => getAllPosts(), []);
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
const getAllPosts = async () => {
try {
const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
setPosts(res.data);
}
catch (e) {
setError(e);
}};
return (
<div>
{error && <div>Error Occured!</div>}
{!error && (
<div className="row" data-testid="posts">
{posts.length > 0 &&
posts.map((post) => <Post key={post.id} post={post} />)}
</div>
)}
</div>
);
};
export default Posts;
Step -4
Create a folder named Mocs inside src and create below js files inside it
- errorHandler.js
- handler.js
- post.response.js
- server.js
errorHandler.js(Will intercept error handling or API failing case)
import {rest} from 'msw';
export const errorHandlers = [
rest.get('https://jsonplaceholder.typicode.com/posts', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({success: false, message: 'Error Occured!!!!'}),
)
})
]
handler.js(Will intercept API Success case)
import {rest} from 'msw';
import {posts} from './post.response'
export const handlers = [
rest.get('https://jsonplaceholder.typicode.com/posts', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json(posts),
)
})
]
post.response.js(Mocked Data)
export const posts = [
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}]
server.js (This is the main file that has control, which means this file will decide which file(error or success case) needs to serve when
as default I am serving here success case(handlers.js)
import {setupServer} from 'msw/node'
import {handlers} from './handlers'
export const server = setupServer(...handlers)
Now Setup is ready, Let's perform unit testing
Create a post.spec.ts file
import {render, screen} from '@testing-library/react';
import Posts from './Posts';
import {posts} from '../../mocks/post.response'
import {server} from '../../mocks/server
import {errorHandlers} from '../../mocks/errorHandler'
// test api success case
test('get post data', async () => {
render(<Posts />);
const title = posts[0].title;
const linkElement = await screen.findByText(title);
expect(linkElement).toBeInTheDocument();
});
// test api error case
test('show error if api fails', async () => {
// as default success response is served from handler.js but for error case we have to mock errorHandlers.js file
//server.use method provide mechanism to switch response
server.use(...errorHandlers)
render(<Posts />);
const errorMessage = await screen.findByText(/Error Occured!/i)
expect(errorMessage).toBeInTheDocument()
});
This is all for doing unit testing for API in react using MSW.
Happy Learning…👏👏👏