This tutorial shows you how to avoid using jest.mock when making components route-aware by using the react-router useHistory Hook.
React-router version 5 introduced a new family of Hooks that have simplified the process of making components route-aware.
useHistory does a great job of showcasing React Hooks by separating access to the react-router history object into a cross-cutting concern and replaces the previous cumbersome access via higher-order components or render-props that are now a thing of the past.
javascript
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
The new Hook makes life much more comfortable, but how do I write tests for this Hook? My initial googling brought back stackoverflow posts like this that advocate for using jest.mock.
javascript
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: jest.fn()
})
}));
I try and avoid this approach at all costs since I want my tests to simulate real usage. jest.mock
will blitz an essential component from the simulated reality and I might miss some critical regressions with this approach.
Instead, I am going to lean heavily on react router’s MemoryHistory
.
I have created this codesandbox:
This includes a simple Hook called useStepper that allows the user to navigate forward and back through several application steps:
Each forward or backward navigation uses the history
object returned from useHistory
to navigate to a new component at a new url:
javascript
export const useStepper = () => {
const history = useHistory();
const nextStepAction = useCallback(() => {
setCurrentStep(index + 1);
}, [getCurrentStepIndex]);
const previousStepAction = useCallback(() => {
setCurrentStep(index - 1);
}, [getCurrentStepIndex]);
useEffect(() => {
const { path } = currentSteps[currentStep];
history.push({
pathname: path,
state: { previousPath: history.location.pathname }
});
}, [currentStep, history]);
// rest
};
There now follows a few easy steps to take control of useHistory
without jest.mock
.
#react #testing #developer