reactjsreact-routerreact-router-domreact-testing-libraryreact-testing

Writing a test to prove location for react-router


I'm fairly new to the world of writing tests for React so forgive the possible newbie question. I've done a fair amount of research and have not found anything that covers this as yet.

I have a really simple BackButton component, all it does is render a button and have an onClick event that goes back in the history. It uses react-router v6 if that is helpful.

import * as React from 'react';
import Stack from '@mui/material/Stack';
import { ArrowCircleLeftOutlined } from '@mui/icons-material';
import { Link, useNavigate } from 'react-router-dom';

export default function BackButton() {
    const navigate = useNavigate();
    return (
        <Stack direction="row" spacing={1}>
            <Link onClick={() => navigate(-1)} aria-label="back" data-testid="backButton">
                <ArrowCircleLeftOutlined />
            </Link>
        </Stack>
    );
}

I've written a test for this using Testing Library but can't seem to figure out a way to see what the location changed to. window.location.href just returns http://localhost

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import BackButton from './BackButton';

it('has a href that refers to the previous page', () => {
    const initialLink = "/foo";

    render(
        <MemoryRouter initialEntries={[initialLink]}>
            <BackButton />
        </MemoryRouter>
    );

    const element = screen.getByTestId('backButton');
    expect(element.getAttribute('href')).toBe(initialLink);
    fireEvent.click(element);
    // How do I test the location changed?
})

Solution

  • After being pointed in the direction of mocking useNavigate (Thanks @DrewReece) I was then able to check to see how many times and how useNavigate was called. The test I ended up with is below. I still had to use react-testing-library so I could fire the event on the screen but very open to learning if this is not the best way to do it.

    import React from 'react';
    import BackButton from './BackButton';
    import { MemoryRouter } from 'react-router-dom';
    import { render, fireEvent, screen } from '@testing-library/react';
    
    const mockedUseNavigate = jest.fn();
    
    jest.mock('react-router-dom', () => ({
       ...jest.requireActual('react-router-dom'),
      useNavigate: () => mockedUseNavigate,
    }));
    
    it('navigates to previous page on click', () => {
        render(<MemoryRouter><BackButton /></MemoryRouter>);
    
        const element = screen.getByTestId('backButton');
        fireEvent.click(element);
    
        expect(mockedUseNavigate).toBeCalledTimes(1);
        expect(mockedUseNavigate).toBeCalledWith(-1);
    })