react-router-dom의 useNavigate 및 Link를 사용하는 컴포넌트의 테스트 에러

react-router-dom의 useNavigate와 Link를 사용하는 컴포넌트를 Router 컨텍스트에서 테스트하는 방법.

React, React Router, Jest, Testing

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