리액트 테스팅 공부를 하다가 해당 에러를 만나게 되었다. 테스트는 다 통과를 하지만 에러가 일어나는 이상한 현상이다.
우선 해당 에러가 어떤 테스트 코드에서 일어나는 지 확인하기 위해 test.skip
과 test.only
를 사용하여 해당 테스트 코드를 식별하였다.
test.skip("grand total starts at $0.00", () => {
render(<OrderEntry />);
const grandTotal = screen.getByRole("heading", {
name: /Grand total: \$/i,
});
expect(grandTotal).toHaveTextContent("0.00");
});
test.only("grand total starts at $0.00", () => {
render(<OrderEntry />);
const grandTotal = screen.getByRole("heading", {
name: /Grand total: \$/i,
});
expect(grandTotal).toHaveTextContent("0.00");
});
Warning : An update to Options inside a test was not warpped in act(…).
Warning : Can’t perform a React state update on an unmounted component.
위 오류는 언마운트 컴포넌트에서 리액트 상태 업데이트를 수행할 수 없다는 오류다.
원인은 대부분 테스트가 끝난 뒤에 컴포넌트가 바뀌기 때문이다. 일부 비동기 상태 업데이트가 완료되기 전에 테스트 함수가 종료된다.
위에 테스트 코드에서 렌더링하는 <OrderEntry />
컴포넌트의 자식 컴포넌트 (<Options />
)는 axios
호출, 즉 비동기 작업이 있다.
useEffect(() => {
axios
.get(`http://localhost:3030/${optionType}`)
.then((response) => setItems(response.data))
.catch((error) => {
setError(true);
});
}, [optionType]);
테스트에서 명시적으로 컴포넌트를 언마운트 한다. 그렇게 하면 컴포넌트에 수행한 작업에 따라 네트워크 호출이 취소된다.
// Options.js
useEffect(() => {
const controller = new AbortController();
axios
.get(`http://localhost:3030/${optionType}`, { signal: controller.signal })
.then((response) => setItems(response.data))
.catch((error) => {
setError(true);
});
return () => {
controller.abort();
};
}, [optionType]);
// Options.test.js
test("grand total starts at $0.00", () => {
const { unmount } = render(<OrderEntry />);
const grandTotal = screen.getByRole("heading", {
name: /Grand total: \$/i,
});
expect(grandTotal).toHaveTextContent("0.00");
unmount();
});
waitFor를 통해 컴포넌트 업데이트가 완료될때까지 기다릴 수 있다.
// Options.js
useEffect(() => {
axios
.get(`http://localhost:3030/${optionType}`)
.then((response) => setItems(response.data))
.catch((error) => {
setError(true);
});
}, [optionType]);
// Options.test.js
test("grand total starts at $0.00", async () => {
render(<OrderEntry />);
const grandTotal = screen.getByRole("heading", {
name: /Grand total: \$/i,
});
await waitFor(() => {
expect(grandTotal).toHaveTextContent("0.00");
});
});