React 16.8부터 추가되었으며, 컴포넌트 사이의 로직을 재사용, 비즈니스 로직을 분리하여 class 없이 리액트 기능을 활용할수 있게 해줌으로써 컴포넌트와 비즈니스의 결합도를 낮출 수 있다.
NavigationBar 컴포넌트 하위에 존재하는 ConfirmModal 컴포넌트에서 isModalOpened prop 데이터를 제공하는 useConfirmModal 훅을 테스트 해본다.
import { useState } from 'react';
const useConfirmModal = (initialValue = false) => {
const [isModalOpened, setIsModalOpened] = useState(initialValue);
const toggleIsModalOpened = () => {
setIsModalOpened(!isModalOpened);
};
return {
toggleIsModalOpened,
isModalOpened,
};
};
export default useConfirmModal;
해당 훅에서 테스트 해봐야 할 내용이 있다면
리액트 훅의 경우 리액트 컴포넌트 내에서만 호출되어야 정상적으로 실행되므로 testing-library/react에서는 renderHook이라는 api를 제공한다.
// 리액트 훅은 반드시 리액트 컴포넌트 내에서만 호출되어야 정상적으로 실행
it('호출 시 initialValue 인자를 지정하지 않는 경우 isModalOpened 상태가 false로 설정된다.', () => {
// result: 훅을 호출하여 얻은 결과 값을 반환
// rerender: 훅을 다시 렌더링하여 결과를 갱신
const { result, rerender } = renderHook(useConfirmModal);
expect(result.current.isModalOpened).toBe(false);
});
it('호출 시 initialValue 인자를 boolean 값으로 지정하는 경우 해당 값으로 isModalOpened 상태가 설정된다.', () => {
const { result } = renderHook(() => useConfirmModal(true));
expect(result.current.isModalOpened).toBe(true);
});
그런데 만약 리액트 훅에 있는 다른 메소드를 테스트해야한다면 어떻게 하면 될까?
예를 들어서 모달을 켜고 끌때 사용하는 state를 토글하는 toggleIsModalOpened라는 메소드가 제대로 작동하는지 테스트를 하려면 이렇게 act를 사용하여 작성해주면 된다.
it('훅의 toggleIsModalOpened()를 호출하면 isModalOpened 상태가 toggle된다.', () => {
const { result } = renderHook(useConfirmModal);
act(() => {
result.current.toggleIsModalOpened();
});
expect(result.current.isModalOpened).toBe(true);
act(() => {
result.current.toggleIsModalOpened();
});
expect(result.current.isModalOpened).toBe(false);
});
※ act 함수
상호작용(렌더링, 이펙트 등)을 함꼐 그룹화하고 실행해 렌더링과 업데이트가 실제 앱이 동작하는 것과 유사한 방식으로 동작한다.
즉, 테스트 환경에서 act를 사용하면 가상의 돔(jsdom)에 반영되었다는 가정하에 테스트가 가능해지는 것이다.
컴포넌트를 렌더링한 뒤 업데이트를 하는 코드의 결과를 검증하고 싶을 때 사용하면 좋다.
결론은,
테스트 환경에서 컴포넌트 렌더링 결과를 jsdom에 반영하기 위해 act 함수를 호출해야한다.
그건 React Testing Library 내부의 render 함수와 user-event 함수는 내부적으로 act함수를 호출해서 상태를 반영하기 때문이다.