Home > other >  Jest: Test useNavigate hook in functional component
Jest: Test useNavigate hook in functional component

Time:05-16

I'm writing test with Jest and want to test a simple button that serves as logout and redirect you to "/login" onClick.

import {loginService} from "../../utils/loginService";
import React from "react";
import {useNavigate} from "react-router-dom";

const Logout = () => {
    const navigate = useNavigate()

    const logout = e => {
        loginService.logout();
        navigate("/login");
    }

    return (
        <button className={'logout'} onClick={logout}>Logout</button>
    )
}

export default Logout

I was able to write a first test that renders the button and checks if it is there. A second test should check, that on click the browser redirects you to "/login".

import Logout from "../../components/navbar/Logout";
import {BrowserRouter} from "react-router-dom";
import {fireEvent, render} from "@testing-library/react";
import {screen} from '@testing-library/react';

describe('Test Logout Button', () => {
    it('it is rendered', () => {
        render(
            <BrowserRouter>
                <Logout/>
            </BrowserRouter>
        )
        expect(screen.getByText(/logout/i)).toBeInTheDocument()
    })

    it('it redirects after click', () => {
        const mockedUsedNavigate = jest.fn();

        jest.mock('react-router-dom', () => ({
            ...jest.requireActual('react-router-dom'),
            useNavigate: () => mockedUsedNavigate,
        }));

        render(
            <BrowserRouter>
                <Logout/>
            </BrowserRouter>
        )
        const logout = screen.getByText(/logout/i);

        fireEvent.click(logout);

        // TODO something something check useNavigate
        // https://blog.logrocket.com/testing-react-router-usenavigate-hook-react-testing-library/
        expect(mockedUsedNavigate).toBeCalledTimes(1);
    })
})

This however fails:

expect(jest.fn()).toBeCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

I would assume that I would somehow have to pass the mock to my component. How would I do that or do I even have to do that? How do I check that useNavigate has redirectet to "/login", as far as I'm concerned <BrowserRouter /> does not have a history attribute

CodePudding user response:

jest.mock should be done outside of your unit tests as it needs to mock out a dependency before it is evaluated by the code you want to test. In your example, react-router-dom library is first evaluated by the Logout component and then your code attempts to mock it out which is too late. So you should move the following code outside of the describe:

const mockedUsedNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useNavigate: () => mockedUsedNavigate,
}));

Ofcourse, all of your tests would then be using the same instance of mockedUsedNavigate which can be error prone, so you should reset it before each unit test by adding it to a beforeEach as follows:

beforeEach(() => {
  mockedUsedNavigate.mockReset();
});
  • Related