I am learning Unit tests with React (new to both), I am stumped at why this isn't working
I want to Test my App.js
import "./App.css";
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./Components/Home";
import PokemonDetail from "./Components/PokemonDetail";
function App() {
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/:offset?" element={<Home />} />
<Route path="/pokemon-detail/:id" element={<PokemonDetail />} />
</Routes>
</BrowserRouter>
);
}
export default App;
Here is my Unit test
import React from "react";
import { render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import App from "../../App";
import "@testing-library/jest-dom";
describe("App", () => {
it("should render Home component by default", () => {
render(
<MemoryRouter initialEntries={["/"]}>
<App />
</MemoryRouter>
);
const homeElement = screen.getByTestId("home");
expect(homeElement).toBeInTheDocument();
});
it("renders the PokemonDetail component when the URL is /pokemon-detail/:id", () => {
render(
<MemoryRouter initialEntries={["/:offset?"]}>
<App />
</MemoryRouter>
);
const pokemonDetailElement = screen.getByTestId("pokemon-detail-id");
expect(pokemonDetailElement).toBeInTheDocument();
screen.debug();
});
});
The error I am getting is
Error: Uncaught [Error: You cannot render a <Router> inside another <Router>. You should never have more than one in your app.]
But I am only using it in the App
and no where else. I get initiating the MemoryRouter
is basically wrapping a router around the router inside the App's router, but how can I test offset etc without?
Yes, react-router@6
it is an invariant violation to render any router inside another router.
Promote the BrowserRouter
out of the App
component so App
renders just the routes and can be wrapped by any other router during unit testing.
index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<Router basename={process.env.PUBLIC_URL}>
<App />
</Router>
</StrictMode>
);
App.jsx
import "./App.css";
import React from "react";
import { Route, Routes } from "react-router-dom";
import Home from "./Components/Home";
import PokemonDetail from "./Components/PokemonDetail";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/:offset?" element={<Home />} />
<Route path="/pokemon-detail/:id" element={<PokemonDetail />} />
</Routes>
);
}
export default App;
The unit test files can now correctly wrap the App
component with any router more suitable for unit testing, e.g. the MemoryRouter
.
import { render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import App from "../../App";
describe("App", () => {
it("should render Home component by default", () => {
render(
<MemoryRouter initialEntries={["/"]}>
<App />
</MemoryRouter>
);
const homeElement = screen.getByTestId("home");
expect(homeElement).toBeInTheDocument();
});
it("renders the PokemonDetail component when the URL is /pokemon-detail/:id", () => {
render(
<MemoryRouter initialEntries={["/:offset?"]}>
<App />
</MemoryRouter>
);
const pokemonDetailElement = screen.getByTestId("pokemon-detail-id");
expect(pokemonDetailElement).toBeInTheDocument();
screen.debug();
});
});