Search code examples
reactjsreact-router-domvitest

Vitest React Router mock not called


I'm migrating from jest to vitest and encountered an issue while checking if the navigate() function returned from the useNavigate() hook was called:

vitest setup.ts:

import matchers from '@testing-library/jest-dom/matchers';
import { expect, vi } from 'vitest';

expect.extend(matchers);

export const mockNavigate = vi.fn();

vi.mock('react-router-dom', async () => {
    const router = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
    return {
        ...router,
        useNavigate: vi.fn().mockReturnValue(mockNavigate),
    };
});

Component test:

import React from 'react';
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { SigninComponent } from '../../../../../../src/features/auth/components/signin.component';
import { renderWithProviders } from '../../../../utils';
import { mockNavigate } from '../../../../setup';

describe('Signin Component', () => {
   it('navigates to signup page', async () => {
        renderWithProviders(<SigninComponent />);
   
        userEvent.click(screen.getByTestId('test-nav-btn'));
        await screen.findByText('AppName');

        expect(mockNavigate).toHaveBeenCalledWith('/signup');
    });
});

Render with providers helper:

import React, { ReactNode } from 'react';
import { render } from '@testing-library/react';
import { gqlClient } from '../../src/common/gql/gql-auth';
import { Provider } from 'react-redux';
import { ApolloProvider } from '@apollo/client';
import { MemoryRouter } from 'react-router-dom';
import { store } from '../../src/common/state/store';

export const renderWithProviders = (component: ReactNode) => {
    return render(
        <Provider store={store}>
            <ApolloProvider client={gqlClient}>
                <MemoryRouter>{component}</MemoryRouter>
            </ApolloProvider>
        </Provider>,
    );
};

Encountered error:

AssertionError: expected "spy" to be called with arguments: [ '/signup' ]
Received:
Number of calls: 0

Solution

  • depending on which version of user-event you are using you may need to await the click in your test. If you are using @testing-library/react v14 then you need to follow the guide here: Writing tests with userEvent @testing-library/react v14

    so you import user-event as 'userEvent' and in your test setup you store the return value from userEvent.setup() init function in a variable called 'user'

    then await the user.click() call in your test.

    import userEvent from '@testing-library/user-event'
    
    // inlining
    test('trigger some awesome feature when clicking the button', async () => {
      const user = userEvent.setup()
      // Import `render` and `screen` from the framework library of your choice.
      // See https://testing-library.com/docs/dom-testing-library/install#wrappers
      render(<MyComponent />)
    
      await user.click(screen.getByRole('button', {name: /click me!/i}))
    
      // ...assertions...
    })
    

    but as Reese mentioned, you're effectively testing the RTL internal code.