react-router-dom의 useNavigate 및 Link를 사용하는 컴포넌트의 테스트 에러
react-router-dom의 useNavigate와 Link를 사용하는 컴포넌트를 Router 컨텍스트에서 테스트하는 방법.
describe("Test Describe", () => {
const user = userEvent.setup();
it("Test Component", async () => {
render(
<TestComponent1
selected={true}
name={"TEST"}
/>
);
});
});react-router-dom의 useNavigate 훅을 사용하는 컴포넌트를 테스트하기 위해 위와 같이 render 함수를 통해 곧바로 렌더링하는 경우, “useNavigate() may be used only in the context of a <Router> component.” 에러가 발생한다.
useNavigate 훅은 오직 Router 컨텍스트 내부에서만 사용할 수 있다는 뜻이다. 아마 브라우저 히스토리와 같은 외부 소스에 의존하는 것이 원인으로 추측된다.
react-router-dom은 이를 위해 MemoryRouter라는, 브라우저 환경과 같이 외부 소스에 연결되지 않은 라우터를 제공하며, 이는 테스트와 같이 외부 환경을 완벽히 제어해야 하는 시나리오에서 이상적이라고 한다.
MemoryRouter v6.20.1 A stores its locations internally in an array. Unlike and , it isn't tied to an external source, like the history stack…
reactrouter.com
따라서 아래와 같이 react-router-dom의 MemoryRouter 컴포넌트로 감싸준 뒤에 렌더링을 시도하면 이 에러에서 회피하여 테스트를 정상적으로 수행할 수 있다.
describe("Test Describe", () => {
const user = userEvent.setup();
it("Test Component", async () => {
render(
<MemoryRouter>
<TestComponent1
selected={true}
name={"TEST"}
/>
</MemoryRouter>
);
const addButton = screen.getByRole("button");
expect(addButton).toBeInTheDocument();
});
});describe("Test Describe", () => {
const user = userEvent.setup();
it("Test Component", async () => {
render(
<TestComponent2
selected={true}
name={"TEST"}
/>
);
});
});TypeError: Cannot destructure property 'basename' of 'React__namespace.useContext(...)' as it is null.
8 |
9 | it("Test Component", async () => {
> 10 | render(
| ^
11 | <TestComponent2
12 | selected={true}
13 | name={"TEST"}또한 react-router-dom의 <Link /> 컴포넌트를 사용하는 컴포넌트의 경우에도 곧바로 렌더링을 시도하는 경우에도 위와 같은 오류가 발생한다.
해당 컴포넌트 내부에서 라우팅을 위해 react-router-dom의 <Link /> 컴포넌트를 사용하는데, 이 경우 Link 컴포넌트가 Router 컴포넌트로 감싸지 않은 것을 원인으로 추측할 수 있었다.
따라서, 이 경우에도 렌더링하려는 컴포넌트를 react-router-dom의 <MemoryRouter /> 컴포넌트로 감싸주면 에러에서 벗어나 테스트를 수행할 수 있다.
describe("Test Describe", () => {
const user = userEvent.setup();
it("Test Component", async () => {
render(
<MemoryRouter>
<TestComponent2
selected={true}
name={"TEST"}
/>
</MemoryRouter>
);
const anchor = screen.getByRole("link");
expect(anchor).toBeInTheDocument();
});
});References
MemoryRouter v6.20.1 A stores its locations internally in an array. Unlike and , it isn't tied to an external source, like the history stack…
reactrouter.com
원문: Medium