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



我正在尝试在新的useHistory钩子中模拟history.pushreact-router并使用@testing-library/react.我只是像这里的第一个答案一样嘲笑了这个模块:如何使用新的反应路由器钩子测试组件?

所以我正在做:

//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不叫...我做错了什么?

在模块范围内使用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

你实际上不需要模拟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');
});

假设您在 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>              

);
};

现在在订单测试中.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" });
});

});

对于任何使用react-router-domV4 的人来说,这对我有用:

// 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")
})
})

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

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

最新更新