Test Nextjs <Link /> with Jest + Testing-Library
Asked Answered
A

1

10

When I'm testing the <Link /> behavior, expecting it to redirect to a certain route, there's a TypeError (Cannot read property 'push' of null).

This is the component I'm currently testing:

import React from "react"
import Link from "next/link"

const Sandbox = () => {
  return (
    <div>
      <Link href="/about">
        <a data-testid="mytest">Click Me</a>
      </Link>
    </div>
  )
}

export default Sandbox

And this is the test I'm running:

import React from "react"
import { render, fireEvent } from "@testing-library/react"
import { useRouter } from "next/router"
import Sandbox from ".."

jest.mock("next/router", () => ({
  useRouter: jest.fn(),
}))

describe("Sandbox", () => {
  it.only("should navigate accordingly", () => {
    const push = jest.fn()
    useRouter.mockImplementationOnce(() => ({
      asPath: "/",
      push,
    }))

    const { getByTestId } = render(<Sandbox />)

    const mytest = getByTestId("mytest")
    fireEvent.click(mytest)
    expect(push).toHaveBeenCalledWith("/about")
  })
})

I believe that I've mocked everything I need, so I don't really understand why the router can't actually "push". What am I missing here?

Awakening answered 17/2, 2021 at 10:9 Comment(4)
how about to mock Link component?Gadget
If I mock link component like this: jest.mock('next/link', () => ({ children }) => children); ...push wont be called.Awakening
can you try jest.mock('next/link', () => ({ Link: 'Link',})); This might not be good way because it can prevent you to test some other things, but maybe it can help you to overcome what you need.Gadget
This works for me: github.com/vercel/next.js/issues/16864#issuecomment-702069418Fatso
M
2

Turns out the module that has to be mocked is slightly different (https://github.com/vercel/next.js/issues/7479#issuecomment-797811147)

Had this problem myself so, based on one of my tests, this should run:


import { render, fireEvent, screen } from "@testing-library/react"
import { useRouter } from "next/router"
import Sandbox from ".."
  
jest.mock("next/dist/client/router", () => ({
   useRouter: jest.fn(),
}))
 
describe("Sandbox", () => {
  const mockPush = jest.fn(() => Promise.resolve(true));
   
  beforeAll(() => {
    useRouter.mockReturnValue({
      asPath: "/",
      query: {},
      push: mockPush,
      prefetch: () => Promise.resolve(true)
    })
  })
    
  test("should navigate accordingly", () => {
    
    render(<Sandbox />)
   
    const mytest = screen.getByTestId("mytest")
    fireEvent.click(mytest)
   
    expect(mockPush).toHaveBeenCalledWith("/about", expect.anything(), expect.anything())
  })
})

I've added expect.anything(), because the push function could be called with its other parameters (https://nextjs.org/docs/api-reference/next/router#routerpush)

Merissa answered 30/3, 2023 at 14:0 Comment(1)
OMG, I'm your big fan. Holy. I was almost giving up of my test. THANK YOU SO MUCH!Wolverhampton

© 2022 - 2024 — McMap. All rights reserved.