Reputation: 1028
I am trying to mock history.push
inside the new useHistory
hook on react-router
and using @testing-library/react
. I just mocked the module like the first answer here: How to test components using new react router hooks?
So I am doing:
//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');
});
})
But mockHistoryPush
is not called... What am I doing wrong?
Upvotes: 55
Views: 84584
Reputation: 560
For anyone out there with react-router-dom
V4, this worked for me:
// 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")
})
})
Upvotes: 0
Reputation: 1175
Let suppose you have created File method in Order.JSx to execute a click event.
If you check i am using
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>
);
};
Now in 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" });
});
});
Upvotes: 0
Reputation: 55
This may be helpful. You can use jest.fn() to mock history.push().
const historyMock = { push: jest.fn() }
expect(historyMock.push.mock.calls[0]).toEqual([
{
pathname: "/profile", // URL
search: , // search-data
},
]);
Upvotes: -3
Reputation: 3863
You actually do not need to mock react-router-dom
(at least for v5) as it provides a bunch of testing tools: https://v5.reactrouter.com/web/guides/testing
To check your history is actually changed, you can use createMemoryHistory
and inspect its content:
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');
});
Upvotes: 60
Reputation: 102287
Use jest.mock
in module scope will automatically be hoisted to the top of the code block. So that you can get the mocked version react-router-dom
in NotFound.jsx
file and your test file.
Besides, we only want to mock useHistory
hook, so we should use jest.requireActual()
to get the original module and keep other methods as the original version.
Here is the solution:
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');
});
});
Unit test result with 100% coverage:
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
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58524183
Upvotes: 110