useNavigate
Hook with React Testing LibraryEditor’s note: This article was last updated by Rahul Chhodde on 4 May 2023 to include information the React fireEvent
and userEvent
Hooks, as well as testing the React query parameters.
React Testing Library is a lightweight solution that provides a virtual DOM to interact with and verify the behavior of a React component. Rather than work like a test runner, this testing library requires a tool like Jest to implement automated testing in React.
Using React Testing Library, popularly known as RTL in the community, is the recommended way to test your apps because it is actively maintained, well-documented, fast, flexible, and powerful enough to write comprehensive and reliable tests.
From React 18 onwards, React Hook Testing Library is included in the React Testing Library, and you can access it only through RTL. Learn more here.
In this article, we will cover testing scenarios of the useNavigate
Hook from React Router 6 with RTL and Jest. We will unit test the routes and briefly discuss the different features that RTL offers to simplify the process of making components route-aware.
Jump ahead:
useNavigate
Hook
useNavigate
HookuseLocation
fireEvent
and userEvent
methodsuseNavigate
HookReact Router v6+ now uses the useNavigate
Hook instead of the useHistory
Hook. You can use useNavigate
to go to a specific path or move back and forth in the browser history.
To add routes to your React app, you need to install the react-router-dom
package. Once you have installed the package, you can import the Route
component from it. The Route
component takes two props: path
and component
. The path
prop is the URL path that the route should match, and the component
prop is the component that should be rendered when the route matches.
For example, the following code would add a route that matches the /
path and renders the Home
component:
import { Routes, Route } from "react-router-dom"; import { Home } from "./components/Home"; import { About } from "./components/About"; function App() { return ( <> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </> ); } export default App;
Let’s implement the useNavigate
Hook. The example below shows how easy it is to navigate to other routes by using the useNavigate
Hook:
import { useNavigate } from "react-router-dom"; function HomeButton() { const navigate = useNavigate(); function handleClick() { navigate("/home"); } return ( <button type="button" onClick={handleClick}> Go home </button> ); }
Here is the live, working version of our project, which also includes the rest of the examples we covered in this article. See it on CodeSandbox here.
If you add { replace: true }
as a second argument when using the useNavigate
Hook, it works like the older history.replace
method. To move back and forth in the history stack, use the following approach:
function HomeButton() { const navigate = useNavigate(); const goBack() => navigate(-1); const goForward() => navigate(1); }
useNavigate
Hook with jest.mock
Avoid using jest.mock
to test React routes, as React Testing Library offers various methods to test routes without mocking. You can use the render
method to isolate a route, the getBy*
method to locate elements in the rendered page, and the fireEvent
and userEvent
Hooks to trigger different events.
useNavigate
HookAs discussed before, React Router 6 and above now uses the useNavigate
Hook instead of the useHistory
Hook. You can no longer set the history
prop to the router anymore. The history API is buggy and must have been discontinued in the newer version of React in favor of simplifying the complicated routing practices, so relying on it for routing is not a good idea.
Here’s a straightforward application to show you how to test routes with useNavigate
. The application has different route components, and we used RTL and Jest features to test each one:
import { fireEvent, render, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; import { MemoryRouter, BrowserRouter } from "react-router-dom"; import { About } from "./About"; describe("Demonstrating some useNavigate() tests ", () => { it("Renders the About component", () => { const { getByText } = render( <BrowserRouter> <About /> </BrowserRouter> ); const button = getByText(/contact us/i); expect(button).toBeInTheDocument(); fireEvent.click(button); }); });
The above is a test component for the “About” section of our app. Keep in mind that all test components must be in the same directory as the original component so that Jest can detect our tests.
We use the describe
and it
methods from Jest to describe our tests and add some sample assertions. To locate an element with a specific text fragment from the rendered component, we use the RTL getByText
utility. If the text fragment is not found, you’ll see an error while testing.
Next, we use fireEvent
to click the selected button element. You can customize these tests further to fit your own needs.
useLocation
We now know how to test the regular React routes with RTL and Jest. Let’s test a route with query parameters with the help of the useLocation
Hook.
We can grab the current URL with the useLocation
Hook, then get the query string and extract the value from the query parameter using URLSearchParams
, which is part of the Web API and Node.js API:
import { useLocation } from "react-router-dom"; const QueryParamComponent = () => { const location = useLocation(); const id = new URLSearchParams(location.search).get("id"); return ( <div> <h1>Query Parameter Component</h1> <p>ID: {id}</p> </div> ); }; export default QueryParamComponent;
After extracting the value of the id
query parameter from the URL, we added it to the resulting DOM element of this component. Now, let’s add a test for this:
import { render, screen } from "@testing-library/react"; import { MemoryRouter } from "react-router-dom"; import QueryParamComponent from "./QueryParamComponent"; import "@testing-library/jest-dom"; describe("MyComponent", () => { it("renders the ID query parameter from the URL", () => { render( <MemoryRouter initialEntries={["/qpc?id=123"]}> <QueryParamComponent /> </MemoryRouter> ); expect(screen.getByText("ID: 123")).toBeInTheDocument(); }); });
In the above code, we used MemoryRouter
to create a mock router that we could use to test the behavior of the QueryParamComponent
when it receives a query parameter through the location prop. We set its initial path to /query
with an id
query parameter so that our component would receive this information when it was rendered. Again, you can see this for yourself in the CodeSandbox demo’s tests.
fireEvent
and userEvent
methodsThe fireEvent
method can dispatch any DOM event to an element, which helps test how components respond to events. For instance, you can use it to test a button that logs an input field’s value when clicked.
In contrast, userEvent
is a simpler method that mimics how users interact with a component. This is helpful when testing components designed for humans, such as a login form:
... describe("Login component", () => { it("Filling details and clicking buttons in a login gorm", async () => { render( <BrowserRouter> <Login /> </BrowserRouter> ); userEvent.type(screen.getByLabelText("Username"), "hello"); userEvent.type(screen.getByLabelText("Password"), "123"); userEvent.click(screen.getByRole("button", { name: "Submit" })); }); });
To use the userEvent
object, you need to install the @testing-library/user-event
package as an additional dependency.
As I mentioned earlier, you don’t need to use the @testing-library/react-hooks
library anymore with React 18 and above. The RTL now includes built-in methods for testing hooks.
It’s fairly easy to test different hooks using the RTL built-in methods only. Here is an example of how to test a custom hook created with useState
, demonstrating a dynamic count component that increments with a button click:
describe("CountComponent", () => { it("SHOULD render the expected Count Component", () => { const { getByText } = render( <BrowserRouter> <CountComponent /> </BrowserRouter> ); const heading = getByText(/the count component/i); const count = getByText(/the count is 0/i); expect(heading).toBeInTheDocument(); expect(count).toBeInTheDocument(); }); it("SHOULD increment the count when the button is CLICKED", () => { const { getByText } = render( <BrowserRouter> <CountComponent /> </BrowserRouter> ); const button = getByText(/increment Count/i); const count = getByText(/the count is 0/i); fireEvent.click(button); expect(count).toHaveTextContent(/the count is 1/i"); }); });
This component and its test are only available in the CodeSandbox example.
With the ever-changing React ecosystem, there are new tools available to help you write better tests. I don’t recommend using Jest mocks to test routes, as this can give the illusion of testing without actually testing anything.
In this article, we explored how to test the useNavigate
Hook that was introduced in React Router v6. I hope you enjoyed this article, and feel free to leave a comment if you have any questions. Happy testing!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
3 Replies to "Testing the React Router <code>useNavigate</code> Hook with React Testing Library"
It seems the history prop does not exist on Router in v6
This. I was enthusiastic when I read the article was written in 2022, hoping that it would cover react router v6.
How to write tests if a react component contains both useLocation and useNavigate