首页 > 解决方案 > 如何在 react-router-dom 中模拟 useNavigate 钩子

问题描述

我有一个自定义钩子,它使用useNavigate来自react-router-dom. 我只想添加一个测试来检查是否将预期值传递给了钩子。该钩子仅在 URL 中存储和检索查询参数的值。

这是我的自定义钩子的样子 -

import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useSearchParam } from 'react-use';

// Default value for the tokens
export const END_PAGE_TOKEN = '';

// Stores pagination tokens in react state and URL query param
export const usePaginationParams = () => {

  // Gets 'token' query param from the URL
  const token = useSearchParam('token');
  const navigate = useNavigate();

  return {
    getCurrentPageToken: () => decodeURIComponent(token || END_PAGE_TOKEN),
    setCurrentPageToken: (pageToken: string) => {
      if (pageToken && pageToken !== END_PAGE_TOKEN) {
        // Sets 'token' query param to the URL
        navigate(`?token=${encodeURIComponent(pageToken)}`);
      } else {
        // Go to the first page
        navigate('');
      }
    },
  };
};

这就是我测试它的方式 -

import { renderHook } from '@testing-library/react-hooks';
import { usePaginationParams } from './usePaginationParams';
import { useNavigate } from 'react-router-dom';
import { useSearchParam } from 'react-use';

let mockedNavigate = jest.fn();

jest.mock('react-router-dom', () => {
  mockedNavigate = jest.fn();
  return {
    __esModule: true,
    useNavigate: mockedNavigate,
  };
});

jest.mock('react-use', () => ({
  useSearchParam: jest.fn(),
}));

const mockedUseSearchParam = useSearchParam as jest.MockedFunction<typeof useSearchParam>;
const mockedUseNavigate = useNavigate as jest.MockedFunction<typeof useNavigate>;

const TEST_TOKEN = 'TEST_TOKEN';

describe('usePaginationParams', () => {
  beforeEach(() => {
    if (mockedUseNavigate) {
      mockedUseNavigate.mockClear();
    }
    if (mockedNavigate) {
      mockedNavigate.mockClear();
    }
    if (mockedUseSearchParam) {
      mockedUseSearchParam.mockClear();
    }
  });
  it('reads query param correctly from URL', () => {
    mockedUseSearchParam.mockReturnValueOnce(TEST_TOKEN);
    const { result } = renderHook(() => {
      const { getCurrentPageToken } = usePaginationParams();
      return getCurrentPageToken();
    });
    expect(mockedUseSearchParam).toBeCalledTimes(1);
    expect(mockedUseNavigate).toBeCalledTimes(1);
    expect(result.current).toBe(TEST_TOKEN);
  });
  it('sets query param correctly to URL', () => {
    mockedNavigate.mockImplementationOnce((to: string) => {
      console.log('mocked to ', to);
    });
    renderHook(() => {
      const { setCurrentPageToken } = usePaginationParams();
      setCurrentPageToken(TEST_TOKEN);
    });
    // expect(mockedNavigate).toBeCalledTimes(1);
    expect(mockedNavigate).toBeCalledWith(`?token=${TEST_TOKEN}`);
  });
});

第一个测试通过,但第二个测试失败并出现以下错误 -

expect(jest.fn()).toBeCalledWith(...expected)

Expected: "?token=TEST_TOKEN"

Number of calls: 0

这意味着mockedNavigate没有被调用并且console.log永远不会被打印。有什么方法可以用预期的参数测试钩子的调用吗?

标签: javascriptreactjstypescriptjestjsreact-router-dom

解决方案


我正在测试错误的模拟。钩子返回一个函数,因此useNavigate()我必须直接模拟它。我只是将其更改为以下代码并开始传递。

it('sets query param correctly to URL', () => {
    const mockImpl = jest.fn().mockImplementation((to: any) => {
      console.log('mocked to ', to);
    });
    mockedUseNavigate.mockImplementationOnce(() => mockImpl);
    renderHook(() => {
      const { setCurrentPageToken } = usePaginationParams();
      setCurrentPageToken(TEST_TOKEN);
    });
    expect(mockImpl).toBeCalledTimes(1);
    expect(mockImpl).toBeCalledWith(`?token=${TEST_TOKEN}`);
  });

推荐阅读