Engineering

Mocking in Jest for Next.js: A Comprehensive Guide

Author

Abhishek Chaubey

Date Published

Curving abstract shapes with an orange and blue gradient

When writing unit tests for a Next.js application, isolating dependencies is crucial for ensuring that tests remain focused and fast. Mocking in Jest provides a way to replace real implementations of modules, functions, or objects with controlled versions, allowing you to test specific behaviors without relying on external dependencies. In this article, we'll dive deep into various mocking techniques with practical examples tailored for Next.js applications.

Mocking API Calls with Axios

In many Next.js projects, you’ll interact with external APIs, and using Axios is a common choice for making HTTP requests. However, during tests, you don't want to make real API calls, so mocking Axios allows you to simulate network requests and focus on testing your logic.

Example: Mocking Axios for GET Requests

Let’s assume you have a function that fetches data from an API:

1// lib/api.ts
2import axios from 'axios';
3
4export const fetchData = async () => {
5 const response = await axios.get('/api/data');
6 return response.data;
7};

In your tests, you can mock Axios to return a predefined response:


1// tests/lib/api.test.ts
2import axios from 'axios';
3import { fetchData } from '@/lib/api';
4
5jest.mock('axios'); // Mock Axios globally
6const mockedAxios = axios as jest.Mocked<typeof axios>;
7
8describe('fetchData', () => {
9 it('should fetch and return data', async () => {
10 const mockResponse = { data: { message: 'Hello, world!' } };
11 mockedAxios.get.mockResolvedValue(mockResponse); // Mock the GET request
12
13 const data = await fetchData();
14 expect(data).toEqual({ message: 'Hello, world!' });
15 expect(mockedAxios.get).toHaveBeenCalledWith('/api/data');
16 });
17});

Explanation:

jest.mock('axios'): This tells Jest to mock the axios module.

mockResolvedValue: This simulates a successful API response with the given mockResponse.

toHaveBeenCalledWith: This checks if the Axios get method was called with the correct URL.

Mocking Next.js Router

The Next.js useRouter hook is commonly used for navigation and retrieving route-specific data. When writing tests for components that rely on routing, it’s important to mock this hook to avoid running tests in a real routing environment.

Example: Mocking useRouter for Navigation

1import { useRouter } from 'next/router';
2
3const MyComponent = () => {
4 const router = useRouter();
5 return <div>{router.route}</div>;
6};
7
8export default MyComponent;

To mock useRouter in a test:

1// tests/components/MyComponent.test.tsx
2import { render, screen } from '@testing-library/react';
3import { useRouter } from 'next/router';
4import MyComponent from '@/components/MyComponent';
5
6// Mock the useRouter hook
7jest.mock('next/router', () => ({
8 useRouter: jest.fn(),
9}));
10
11describe('MyComponent', () => {
12 it('renders the current route', () => {
13 // Mock the useRouter hook's return value
14 (useRouter as jest.Mock).mockReturnValue({ route: '/home' });
15
16 render(<MyComponent />);
17 const routeElement = screen.getByText('/home');
18 expect(routeElement).toBeInTheDocument();
19 });
20});
21

Explanation:

jest.mock('next/router'): This tells Jest to mock the Next.js useRouter hook.

(useRouter as jest.Mock).mockReturnValue: This sets a mocked return value for the useRouter hook, so we can simulate a route.

getByText: This is used to check if the current route is rendered correctly.

Mocking Date and Time

In some cases, you may need to mock the current date or time, especially when testing components that depend on time-sensitive data (e.g., a countdown timer or an event).

Example: Mocking Date for Testing Time-Based Logic

1// components/DateDisplay.tsx
2const DateDisplay = () => {
3 const currentDate = new Date().toLocaleDateString();
4 return <div>{currentDate}</div>;
5};
6
7export default DateDisplay;

In your tests, you can mock the Date object to control the current date:

1// tests/components/DateDisplay.test.tsx
2import { render, screen } from '@testing-library/react';
3import DateDisplay from '@/components/DateDisplay';
4
5// Mock the Date object
6const mockDate = new Date(2024, 11, 25); // December 25, 2024
7global.Date = jest.fn(() => mockDate); // Override Date globally
8
9describe('DateDisplay', () => {
10 it('displays the mocked current date', () => {
11 render(<DateDisplay />);
12 const dateElement = screen.getByText('12/25/2024');
13 expect(dateElement).toBeInTheDocument();
14 });
15});

Explanation:

global.Date = jest.fn(() => mockDate): This mocks the global Date object to always return the mocked date during tests.

The test then ensures that the rendered date matches the mocked date.

Mocking Browser-Specific APIs (e.g., window or localStorage)

In a Next.js project, you may encounter browser-specific APIs such as window or localStorage, which are not available in a Node.js environment. When writing tests that interact with these APIs, you can mock them to simulate their behavior.

Example: Mocking localStorage

1// components/LocalStorageComponent.tsx
2const LocalStorageComponent = () => {
3 const saveData = () => {
4 localStorage.setItem('username', 'JohnDoe');
5 };
6
7 return <button onClick={saveData}>Save Data</button>;
8};
9
10export default LocalStorageComponent;

In your test, you can mock localStorage like this:

1// tests/components/LocalStorageComponent.test.tsx
2import { render, screen, fireEvent } from '@testing-library/react';
3import LocalStorageComponent from '@/components/LocalStorageComponent';
4
5// Mock localStorage
6beforeAll(() => {
7 const localStorageMock = {
8 getItem: jest.fn(),
9 setItem: jest.fn(),
10 removeItem: jest.fn(),
11 clear: jest.fn(),
12 };
13 global.localStorage = localStorageMock; // Override localStorage globally
14});
15
16describe('LocalStorageComponent', () => {
17 it('saves data to localStorage when the button is clicked', () => {
18 render(<LocalStorageComponent />);
19 const button = screen.getByText('Save Data');
20 fireEvent.click(button);
21 expect(localStorage.setItem).toHaveBeenCalledWith('username', 'JohnDoe');
22 });
23});

Explanation

beforeAll: Sets up the mock for localStorage before any tests run.

global.localStorage = localStorageMock: Replaces the global localStorage with the mock object.

fireEvent.click(button): Simulates a button click that triggers the localStorage.setItem method.

Conclusion

Mocking is an essential technique when writing unit tests in Jest for Next.js projects. It allows you to isolate dependencies, simulate behavior, and test components in isolation. Whether you’re working with APIs, browser-specific features, or routing, Jest provides powerful tools to mock various aspects of your application, making your tests faster and more reliable.

By using the techniques and examples outlined in this article, you can handle complex testing scenarios with ease. Mocking helps ensure that your components behave as expected without relying on external services or resources, making it a crucial part of the testing process.