본문 바로가기
React

React Testing Library를 이용한 Custom Hooks 테스팅

by 도현위키 2022. 3. 28.

이번에는 React-Testing-Library를 이용해서 Custom Hooks를 테스팅해보겠다. Custom Hooks는 일반적인 함수처럼 테스트 코드를 작성할 수 없다. 그 이유는 Custom Hooks는 React에서 제공하는 Hooks(useState, useEffect...)를 이용한 함수이기 때문이다. 

 

대표적인 Custom Hooks인 useToggle hooks 예시를 보면 다음과 같다.

import { useCallback, useState } from "react";

export default function useToggle(initialState = false) {
  const [state, setState] = useState(initialState);
  const onToggle = useCallback(() => setState(!state), [state]);

  return [state, onToggle, setState] as const;
}

 

일반적인 함수처럼 테스트코드를 작성할 수 없기 때문에 라이브러리를 추가적으로 설치해줘야 한다.

 

@testing-library/react-hooks 설치

터미널에 다음 명령어를 입력하여 라이브러리를 설치하자

$ yarn add @testing-library/react-hooks 

or 

$ npm install @testing-library/react-hooks

 

useState를 이용한 커스텀 훅 테스트

useState hooks를 이용한 커스텀 훅의 테스트를 하기 위해서 위에서 예시를 들었던 useToggle과 input의 상태를 바꿔주는 useInput hooks를 테스팅해보겠다.

 

먼저 useToggle을 테스트를 진행해보겠다.

import { useCallback, useState } from "react";

export default function useToggle(initialState = false) {
  const [state, setState] = useState(initialState);
  const onToggle = useCallback(() => setState(!state), [state]);

  return [state, onToggle, setState] as const;
}

 

테스트 케이스는 다음과 같다.

1) useToggle은 길이가 3인 배열을 리턴한다. [state, onToggle, setState]

2) 매개변수로 initialState 값을 입력하지 않으면 기본 state 값은 false로 설정된다.

3) 매개변수로 initialState 값을 입력하면 state에 그 값이 설정된다.

4) onToggle 함수를 이용해서 state 값을 toggle 할 수 있다.

5) setState 함수를 이용해서 직접 state 값을 변경할 수 있다.

 

 

작성한 테스트 코드는 다음과 같다.

import { act, renderHook } from "@testing-library/react-hooks";
import useToggle from "../useToggle";

describe("useToggle", () => {
  test("useToggle은 길이가 3인 배열을 리턴한다. (state, onToggle, setState)", () => {
    const { result } = renderHook(() => useToggle(false));
    expect(result.current).toHaveLength(3);
  });

  test("매개변수로 initialState 값을 입력하지 않으면 기본 state 값은 false로 설정된다.", () => {
    const { result } = renderHook(() => useToggle());
    expect(result.current[0]).toBe(false);
  });

  test("매개변수로 initialState 값을 입력하면 state에 그 값이 설정 된다.", () => {
    const { result } = renderHook(() => useToggle(true));
    expect(result.current[0]).toBe(true);
  });

  test("onToggle 함수를 이용해서 state 값을 toggle 시킬 수 있다.", () => {
    const { result } = renderHook(() => useToggle(false));

    act(() => {
      result.current[1]();
    });

    expect(result.current[0]).toBe(true);
  });

  test("setState 함수를 이용해서 직접 state 값을 변경할 수 있다.", () => {
    const { result } = renderHook(() => useToggle(false));

    act(() => {
      result.current[2](true);
    });

    expect(result.current[0]).toBe(true);
  });
});

 

renderHook 함수를 이용해서 우리가 테스트하고자 하는 custom Hooks를 실행시키면, return 값이 result에 담겨 있다. useToggle hooks에서 return 값이 [state, onToggle, setState] 이기 때문에 이 값이 result로 담겨져 있다.

테스트 코드 내부에서 컴포넌트 렌더링 및 상태 변경은 act 함수 내부에서 실행해야 한다

 

 

다음은 useInput hooks를 테스팅해보자. useInput의 코드는 아래와 같다.

import React, { useState } from "react";

export default function useInput(initialValue = "") {
  const [value, setValue] = useState(initialValue);

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };

  return [value, onChange, setValue] as const;
}

 

테스트 케이스는 다음과 같다.

1) useInput은 길이가 3인 배열을 리턴한다. [value, onChange, setValue]

2) initialValue를 입력하면 value 값이 설정된다.

3) onChange 함수로 value 값을 변경할 수 있다.

4) setValue 값으로 직접 value를 변경할 수 있다.

 

작성한 테스트 코드는 다음과 같다.

 

import { renderHook, act } from "@testing-library/react-hooks";
import useInput from "../useInput";

describe("useInput", () => {
  test("useInput은 길이가 3인 배열을 리턴한다. (value, onChange, setValue)", () => {
    const { result } = renderHook(() => useInput(""));
    expect(result.current).toHaveLength(3);
  });

  test("initialValue를 입력하면 value 값이 설정 된다.", () => {
    const { result } = renderHook(() => useInput("test"));
    expect(result.current[0]).toBe("test");
  });

  test("onChange 함수로 value 값을 변경할 수 있다", () => {
    const { result } = renderHook(() => useInput(""));

    act(() => {
      result.current[1]({
        target: { value: "테스트입니다" },
      } as React.ChangeEvent<HTMLInputElement>);
    });

    expect(result.current[0]).toBe("테스트입니다");
  });

  test("setValue 값으로 직접 value를 변경할 수 있다", () => {
    const { result } = renderHook(() => useInput(""));

    act(() => {
      result.current[2]("두번째 테스트");
    });

    expect(result.current[0]).toBe("두번째 테스트");
  });
});

 

 

마치며

이렇게 useState 상태를 이용한 custom hooks의 테스팅을 해봤다. 이 외에도 rerender라는 메서드를 이용한 useEffect 테스트 방식도 있는데 자세한 내용은 https://react-hooks-testing-library.com/ 공식문서를 참고하면 많은 기능을 테스트해볼 수 있다.

댓글