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

wsxa1bj1  于 2023-09-28  发布在  Jest
关注(0)|答案(5)|浏览(125)

我试图在react-router上的新useHistory钩子中使用@testing-library/react来模拟history.push。我只是模仿了模块,就像这里的第一个答案一样: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;
//NotFound.test.js
describe('RouteNotFound', () => {
  it('Redirects to correct URL on click', () => {
    const mockHistoryPush = jest.fn();

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

    const { getByRole } = render(
        <MemoryRouter>
          <RouteNotFound />
        </MemoryRouter>
    );

    fireEvent.click(getByRole('button'));
    expect(mockHistoryPush).toHaveBeenCalledWith('/help');
  });
})

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

crcmnpdw

crcmnpdw1#

在模块范围内使用jest.mock将自动提升到代码块的顶部。这样你就可以在NotFound.jsx文件和你的测试文件中获得模拟版本react-router-dom
此外,我们只想模拟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;

NotFound.test.jsx

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');
  });
});

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

PASS  src/stackoverflow/58524183/NotFound.test.jsx
  RouteNotFound
    ✓ Redirects to correct URL on click (66ms)

--------------|----------|----------|----------|----------|-------------------|
File          |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
--------------|----------|----------|----------|----------|-------------------|
All files     |      100 |      100 |      100 |      100 |                   |
 NotFound.jsx |      100 |      100 |      100 |      100 |                   |
--------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.133s, estimated 11s

原始程式码:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58524183

icnyk63a

icnyk63a2#

实际上,您不需要模拟react-router-dom(至少对于v5),因为它提供了一系列测试工具:https://v5.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');
});
x6h2sr28

x6h2sr283#

假设您已经在Order.JSx中创建了File方法来执行单击事件。如果你检查我正在使用

const history = useHistory();

This is react functional component 
import { useHistory } from 'react-router-dom';
        function ReactOrders({ prams }) {
        const history = useHistory();
    
        const viewOrder = (order) => {
            **history.push({**
                pathname: `/order/${order.orderId}/${order.orderCompany}/${order.orderType}`,
            });
        };
    
        const renderOrder = (order, index) => {
            return (           
                    <div className="view-order">                   
  <button className="button button-secondary" onClick={() => viewOrder(order)}>
                                View Order
                            </button>
                        </div>              
               
            );
        };

现在在OrdersTest.js中

import { createMemoryHistory } from 'history';

let history = createMemoryHistory();

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

 describe("and it returns multiple orders", () => {    
             
                beforeEach(() => {                     
                    _component = shallow(                     
                            <Orders {...props} orders={_orderData.salesOrderHeader} fetching={false} />
                        , {
                        context: { router: history }, // Provide the mock history object as context
                    });                  
                });
       
                fit("shows a link to the order details", () => {                  
                    _component.find(".details .view-order .button")
                        .forEach((b, i) => {
                            b.simulate("click");
                        });                
                    expect(mockHistoryPush).toHaveBeenCalledWith({ "pathname": "/order/orderBert/company1/CO" });
                    expect(mockHistoryPush).toHaveBeenCalledWith({ "pathname": "/order/jobbert/company2/SO" });
                });
      
            });
ijxebb2r

ijxebb2r4#

对于任何使用react-router-dom V4的人来说,this对我来说都很有效:

// HomeButton.test.jsx
    import { render, screen, act } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    
    import { HomeButton } from './HomeButton';
    
    describe('HomeButton', () => {
      // the test might throw an error about required properties, it's depend on your component's dependencies.
      const mockHistory = {
        push: jest.fn(),
      }
    
      it('should go to home after click', () => {
        await act(async () => {
          render(
            <Router history={mockHistory}>
              <HomeButton />
            </Router>
          )
    
          userEvent.click(screen.getByTestId('button'))
        })
    
        expect(mockHistory.push).toBeCalledTimes(1)
        expect(mockHistory.push).toBeCalledWith("/home")
      })
    })
blmhpbnm

blmhpbnm5#

这可能会有帮助。您可以使用**jest.fn()**来模拟history.push()。

const historyMock = { push: jest.fn() }   

expect(historyMock.push.mock.calls[0]).toEqual([
        {
          pathname: "/profile", // URL
          search: , // search-data
        },
      ]);

相关问题