An implementation of the Next.js Router that keeps the state of the "URL" in memory (does not read or write to the
address bar). Useful in tests. Inspired
by react-router > MemoryRouter
.
next-router-mock
is a drop-in replacement for next/router
. It exports both a default (singleton) router and
the useRouter
hook.
Install via NPM: npm install --save-dev next-router-mock
Simply drop-in the next-router-mock
like this:
jest.mock('next/router', () => require('next-router-mock'));
// or this:
jest.mock('next/dist/client/router', () => require('next-router-mock'));
Note: it's better to mock
next/dist/client/router
instead ofnext/router
, because bothnext/router
andnext/link
depend directly on this nested path. It's also perfectly fine to mock both.
Here's a full working example:
import router, { useRouter } from 'next/router';
import NextLink from 'next/link';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
jest.mock('next/dist/client/router', () => require('next-router-mock'));
describe('router', () => {
it('supports the `push` and `replace` methods', () => {
router.push('/foo?bar=baz');
expect(router).toMatchObject({
asPath: '/foo?bar=baz',
pathname: '/foo',
query: { bar: 'baz' },
});
});
it('supports url object routes too', () => {
router.push({
pathname: '/foo/[id]',
query: { id: '123', bar: 'baz' },
});
expect(router).toMatchObject({
asPath: '/foo/123?bar=baz',
pathname: '/foo/[id]',
query: { bar: 'baz' },
});
});
it('next/link can be tested too', () => {
render(<NextLink href="/example?foo=bar">Example Link</NextLink>);
fireEvent.click(screen.getByText('Example Link'));
expect(router).toMatchObject({
asPath: '/example?foo=bar',
pathname: '/example',
query: { foo: 'bar' },
});
});
});
By default, next-router-mock
handles route changes synchronously. This is convenient for testing, and works for most use-cases.
However, Next normally handles route changes asynchronously, and in certain cases you might actually rely on that behavior. If that's the case, you can use next-router-mock/async
. Tests will need to account for the async behavior too; for example:
it('next/link can be tested too', async () => {
render(<NextLink href="/example?foo=bar">Example Link</NextLink>);
fireEvent.click(screen.getByText('Example Link'));
await waitFor(() => {
expect(router).toMatchObject({
asPath: '/example?foo=bar',
pathname: '/example',
query: { foo: 'bar' },
});
});
});
useRouter()
router.push(url, as, options)
router.replace(url, as, options)
router.pathname
router.asPath
router.query
router.locale
router.locales
- Works with
next/link
(see Jest notes) router.events
supports:routeChangeStart(url, { shallow })
routeChangeComplete(url, { shallow })
PRs welcome!
These fields have default values; these methods do nothing.
router.isReady
router.route
router.basePath
router.isFallback
router.prefetch()
router.back()
router.beforePopState(cb)
router.reload()
router.events
not implemented:routeChangeError
beforeHistoryChange
hashChangeStart
hashChangeComplete