How to mock Next.js router in Jest tests

Updated: January 1, 2024 By: Guest Contributor Post a comment

Overview

Testing Next.js applications requires a sound strategy to mock the Next.js router for comprehensive and isolated test cases. This tutorial illustrates how to mock the Next.js router in Jest tests effectively, ensuring that you can test components that rely on router functionality without issues.

Setting Up Jest with Next.js

Before we mock the Next.js router, ensure that you have Jest set up in your Next.js project. You can achieve this by installing the necessary packages:

npm install --save-dev jest babel-jest @testing-library/react @testing-library/jest-dom

Next, create a Jest configuration file:

// jest.config.js
module.exports = {
  setupFilesAfterEnv: ['/jest.setup.js'],
  testPathIgnorePatterns: ['/node_modules/', '/.next/'],
  // other configurations
};

And a Jest setup file:

// jest.setup.js
import '@testing-library/jest-dom/extend-expect';
// other setups like the custom matchers

Basic Mocking of Next.js Router

To start mocking the Next.js router, you’ll need to override the useRouter hook provided by Next.js. A simple mock for Jest might look like this:

// __mocks__/next/router.js
const useRouter = jest.fn()
useRouter.mockImplementation(() => ({
  push: jest.fn(),
  route: '/',
  query: {},
  asPath: '',
  // Add more properties here
}));

export default useRouter;

When importing useRouter in your tests, Jest will now use the mocked implementation, allowing you to control the return values and spies on methods like push.

Advanced Mocks and Testing Navigation

For more advanced scenarios, such as testing navigation, you can extend the mock to include more functionality like handling route changes:

// __mocks__/next/router.js
const useRouter = jest.fn()
let routeChangeHandler;

useRouter.mockImplementation(() => ({
  push: async (url) => {
    const routeChangeStart = new Event('routeChangeStart');
    routeChangeHandler && routeChangeHandler(url, routeChangeStart);
    return Promise.resolve(true); // or false based on navigation success
  },
  on: (event, handler) => {
    if (event === 'routeChangeStart') routeChangeHandler = handler;
  },
  // Add more properties and methods as needed
}));

export default useRouter;

This allows for a closer emulation of Next.js router behavior during tests, so that components that listen to route events can be properly tested.

Testing with router props

In Next.js 13+, you may often pass the router as a prop to components. This can be mocked as follows:

// during component testing
import MyComponent from '../components/MyComponent';

const mockRouter = {
  pathname: '/someRoute',
  push: jest.fn(),
  // ... other router properties
};

render(
  
);

Make sure to update the mockRouter object with the necessary properties and functions you expect your component to use or to depend on.

Utilizing next-router-mock for Comprehensive Mocking

If manually mocking seems too cumbersome, or you require a more comprehensive solution, consider using a library like next-router-mock:

npm install --save-dev next-router-mock

This package provides a full mock implementation of the Next.js router for use with Jest, complete with all the router’s methods and properties, ensuring your tests are reliable across different scenarios.

Conclusion

Mocking the Next.js router is crucial in creating isolatable and robust Jest tests for your Next.js application. Whether you opt for a simple manual mock or use a dedicated mocking library, you now have the tools to control and test router behavior effectively within your components’ test suites. Also, remember to review and adapt your mocks to keep up with the changes that come with Next.js updates.