Published on

React Testing Library 로 React Hook 테스트하기

리액트 커스텀 훅을 테스트하기 위해서 여러 가지 방법을 사용할 수 있다.

  1. 라이브러리 없이 훅 테스트하기
  2. 커스텀 훅을 사용하는 테스트 컴포넌트를 만들고 해당 컴포넌트를 테스트한다.
  3. @testing-library/react-hooks의 renderHook을 사용한다.

그중에 3번 testing-library를 사용하여 진행한다.

설치

react component 테스트 시에 설치했던 라이브러리 외에 추가로 다음 github을 참고하여 설치한다.

테스트 작성

  1. count 증감 hook을 테스트 한다.
    • state 업데이트 함수호출을 act로 감싸서 다음 테스트 진행전에 렌더링 업데이트 반영을 기다린다.
test('should display initialValue and increment if passed', () => {
  const { result } = renderHook(() => useCounter(300));

  expect(result.current.count).toBe(300);

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(301);
});
test('v2 - allows customization of the step', () => {
  const { result } = renderHook(useCounter, { initialProps: { step: 2 } });
  expect(result.current.count).toBe(0);

  act(() => result.current.increment());
  expect(result.current.count).toBe(2);

  act(() => result.current.decrement());
  expect(result.current.count).toBe(0);
});
  1. hook 렌더 함수인 renderReadModelHook 를 구현한다.
    • 반복적인 테스트에서 재사용할 수 있다.
const renderReadModelHook = (
  query: ReadModelQuery,
  initialState: any,
  options?: ReduxReadModelHookOptions
) => {
  const {
    result: { current },
  } = renderHook(() => useReduxReadModel(query, initialState, options));
  return current;
};
  1. 마운트시에 API fetch를 수행하는 hook을 테스트한다.
    • fetch를 mocking하고, waitForNextUpdate 로 다음 렌더링 업데이트를 기다린다.
describe('Fetch Data on Mount Hook', () => {
  it('calls fetch with the given id and resource', async () => {
    const expected = { complete: false, id: '1', title: 'something', userId: '2' };
    fetch.mockResponseOnce(JSON.stringify(expected));

    const props = {
      id: '1',
      resource: 'todo',
    };

    const { result, waitForNextUpdate } = renderHook(() => useFetchDataOnMount(props));

    await waitForNextUpdate();

    expect(fetch).toHaveBeenCalledTimes(1);
    expect(fetch).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/todo/1');

    expect(result.current).toEqual(expected);
  });
});
describe('use the fetch', () => {
  it('initial data state is loading and data empty', () => {
    const { result } = renderHook(() => useTheFetch('people'));

    expect(result.current).toStrictEqual({ loading: true, data: null });
  });

  it('data is fetched and not loading', async () => {
    const fakeSWData = { result: [{ name: 'Luke Skywalker' }] };
    getStarWars.mockResolvedValue(fakeSWData);
    const { result, waitForNextUpdate } = renderHook(() => useTheFetch('people'));

    await waitForNextUpdate();

    expect(getStarWars).toBeCalledWith('people');
    expect(result.current).toStrictEqual({
      loading: false,
      data: fakeSWData,
    });
  });
});

참고