如何使用 Jest 使用新的 React Router Hooks 模拟 history.push

Alb*_*ses 34 reactjs jestjs react-router react-router-dom react-testing-library

我正在尝试history.push在新useHistory钩子上模拟react-router并使用@testing-library/react. 我只是像这里的第一个答案一样嘲笑模块:How to test components using new react router hooks?

所以我在做:

//NotFound.js
import * as React from 'react';
import { useHistory } from 'react-router-dom';


const RouteNotFound = () => {
  const history = useHistory();
  return (
    <div>
      <button onClick={() => history.push('/help')} />
    </div>
  );
};

export default RouteNotFound;
Run Code Online (Sandbox Code Playgroud)
//NotFound.js
import * as React from 'react';
import { useHistory } from 'react-router-dom';


const RouteNotFound = () => {
  const history = useHistory();
  return (
    <div>
      <button onClick={() => history.push('/help')} />
    </div>
  );
};

export default RouteNotFound;
Run Code Online (Sandbox Code Playgroud)

mockHistoryPush不叫...我做错了什么?

sli*_*wp2 75

jest.mock在模块作用域中使用会自动被提升到代码块的顶部。所以,你可以得到嘲笑的版本react-router-domNotFound.jsx文件和测试文件。

此外,我们只想模拟useHistory钩子,所以我们应该使用jest.requireActual()获取原始模块并保留其他方法作为原始版本。

这是解决方案:

NotFound.jsx

import React from 'react';
import { useHistory } from 'react-router-dom';

const RouteNotFound = () => {
  const history = useHistory();
  return (
    <div>
      <button onClick={() => history.push('/help')} />
    </div>
  );
};

export default RouteNotFound;
Run Code Online (Sandbox Code Playgroud)

NotFound.test.jsx

import React from 'react';
import { useHistory } from 'react-router-dom';

const RouteNotFound = () => {
  const history = useHistory();
  return (
    <div>
      <button onClick={() => history.push('/help')} />
    </div>
  );
};

export default RouteNotFound;
Run Code Online (Sandbox Code Playgroud)

100% 覆盖率的单元测试结果:

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

const mockHistoryPush = jest.fn();

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useHistory: () => ({
    push: mockHistoryPush,
  }),
}));

describe('RouteNotFound', () => {
  it('Redirects to correct URL on click', () => {
    const { getByRole } = render(
      <MemoryRouter>
        <RouteNotFound />
      </MemoryRouter>,
    );

    fireEvent.click(getByRole('button'));
    expect(mockHistoryPush).toHaveBeenCalledWith('/help');
  });
});
Run Code Online (Sandbox Code Playgroud)

源代码:https : //github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58524183


Sou*_*euh 11

您实际上不需要模拟react-router-dom(至少对于 v5),因为它提供了一堆测试工具:https : //reactrouter.com/web/guides/testing

要检查您的历史记录是否实际更改,您可以使用createMemoryHistory并检查其内容:

import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Menu } from './Menu';
import { createMemoryHistory } from 'history'
import { Router } from 'react-router-dom';

test('triggers path change', () => {
  const history = createMemoryHistory();

  render(
    <Router history={history}>
      <Menu />
    </Router>
  );

  const aboutItem = screen.getByText('About');
  expect(aboutItem).toBeInTheDocument();

  userEvent.click(aboutItem);
  expect(history.length).toBe(2);
  expect(history.location.pathname).toBe('/about');
});
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。模拟反应路由器可能会导致不必要的复杂性。 (2认同)