August 1, 2021 • ☕️ 2 min read
react hook test
custom hook으로 작성한 react query의 동작을 테스트하기 위해 react-hooks-testing-library를 사용했다. 기본적으로 renderHook이라는 메서드를 제공하여, hook 테스트를 위해 별도의 컴포넌트를 작성하여 hook을 호출할 필요가 없다. 내부적으로 HTTP request를 사용하는 hook이나, 복잡한 컴포넌트 로직 내에서 동작하는 hook의 경우에 react-hooks-testing-library를 사용하여 간단하게 테스트를 진행해볼 수 있다.
renderHook에 테스트하고자 하는 hook 함수를 넘겨주고, 반환값인 result를 이용하여 hook 호출의 성공 여부(isSuccess)와 반환된 데이터(data)를 검사한다.
import { renderHook } from "@testing-library/react-hooks";
describe("useRecentFeeds 테스트", () => {
  it("최신 피드를 불러올 수 있다.", async () => {
    const { result, waitFor } = renderHook(() =>
      useRecentFeeds({ filter: FilterType.PROGRESS })
    );
    await waitFor(() => result.current.isSuccess);
    expect(result.current.data).toEqual(mockFeeds);
  });
});hook 실행 후 일정 시간이 지나야 통과되는 비동기적인 실행이라면, renderHook에서 반환되는 waitForNextUpdate 함수를 통해 hook 내부 로직에서 변화가 발생하는 시점까지 기다린 후 expect문을 실행할 수 있다.
import { renderHook } from "@testing-library/react-hooks";
describe("useOAuthLogin 테스트", () => {
  it("github으로부터 액세스 토큰을 받아올 수 있다.", async () => {
    const { waitForNextUpdate } = renderHook(() => useOAuthLogin("github"));
    await waitForNextUpdate();
    expect(localStorage.getItem("accessToken")).not.toBeNull();
  });
  it("google로부터 액세스 토큰을 받아올 수 있다.", async () => {
    const { waitForNextUpdate } = renderHook(() => useOAuthLogin("google"));
    await waitForNextUpdate();
    expect(localStorage.getItem("accessToken")).not.toBeNull();
  });
});테스트에 사용된 hook들은 모두 서버 데이터를 가져오는 react query 코드였는데, 이때 msw(mock-service-worker)를 이용하여 실제 서버로 가는 요청을 가로채 지정한 응답을 반환해줬다. 실제 서버는 언제든지 터지거나 사이드 이펙트가 발생할 수 있기 때문에, 서버를 mocking하여 기대하는 결과값이 나오는지 검사해준다.
rest.get(`${BASE_URL.production}/login/oauth/:type/token`, (req, res, ctx) => {
  return res(
    ctx.status(200),
    ctx.json({
      accessToken: 'mock-access-token-abc-123',
    }),
  );
}),이밖에도 act 함수를 통해 hook 내부에 정의한 함수를 실행시켜줄 수도 있으며, renderHook에 전달되는 hook에 인자를 넘겨줄 수도 있다. 여러모로 유용하게 hook test를 작성할 수 있다!
Ref