render 함수
기능
- 컴포넌트를 document.body에 붙여서 렌더링 시킨다.
반환값
1. query objects
render 함수의 반환 값으로 getByRole을 사용할 수 있다.
const { getByRole } = render(<Counter initialProps={1} />
// 버튼 역할을 하면서 - name을 가진 element가 document에 있는지 찾음
expect(getByRole('button', { name: "-" })).toBeInTheDocument()
screen object를 통해서 getByRole 함수에 접근할 수 있다.
expect(screen.getByRole('button', { name: "+" })).toBeInTheDocument();
2. container
- 리액트 컴포넌트가 렌더링 된 DOM Node
3. baseElement
- container가 들어가 있는 DOM node이며, 기본값은 document body
4. rerender
- props를 변경하여 컴포넌트를 업데이트 처리할 때 사용
test("Counter initialValueChanged", () => {
const { rerender } = render(<Counter initialProps={1} />)
expect(screen.getByRole('display').textContent).toBe("1")
rerender(<Counter initialProps={2} />)
expect(screen.getByRole('display').textContent).toBe("2")
})
5. unmount
- 컴포넌트를 의도적으로 attach된 element에서 unmount 시킬 때 사용
- 메모리 누수를 유발하는 이벤트 핸들러나 컴포넌트가 언마운트 될 때 시행되는 동작을 검증할 때 유용하게 사용된다.
renderHook 함수
기능
- hook을 테스트하는 함수
- hook은 함수형 컴포넌트에서만 호출되어야 한다.
- hook 테스트 코드 잘못된 예시
test('Check initial value of isOpen is false', () => {
const result = useOpen()
expect(result.open).toBe(false)
});
이유) hook은 functional component 안에서만 호출될 수 있다.
해결방법) wrapper 컴포넌트를 이용
test('Check initial value of isOpen is false', () => {
// arrange
let useOpenRes = {} as ReturnType<typeof useOpen>;
const Wrapper = () => {
useOpenRes = useOpen(true)
return <></>;
};
// act
render(<Wrapper />);
// assert
expect(useOpenRes.open).toBe(true);
});
=> 이렇게 할수도 있지만, renderHook 함수를 사용해서 더 깔끔하게 코드를 작성할 수 있다.
test('Check initial value of isOpen is false', () => {
const { result } = renderHook(() => useOpen());
expect(result.current.open).toBe(false);
});
- renderHook으로 Context를 검증할 수 있다.
custom hook이 Context API, redux의 dispatch 같은 Context를 사용하는 경우
정의된 Provider를 가지고 있는 custom wrapper 컴포넌트를 만들고, custom wrapper 컴포넌트를 render hook의 두번째 인자로 넣어주고, 첫번째 인자에는 해당하는 hook을 호출하는 콜백함수를 넣음
- renderHook으로 Context를 검증할 수 있다.
hook이 비동기 동작을 할 때 검증 방벙
예를 들어, setTimeout을 쓰는 경우 jest의 fake Timer를 사용하여 검증을 할 수 있다.
설정과 해지 적용이 필요하다. 해지 시에는 mocking 된 타이머를 없애주고 남아있는 타이머를 모두 소진 시켜야 한다.
describe('useCounter with async test', () => {
// 설정
beforeEach(() => {
jest.useFakeTimers()
})
// 해지
afterEach(() => {
jest.runOnlyPendingTimers() // 남아있는 타이머 소진
jest.useRealTimers()
})
test('should increment counter after delay', async () => {
const { result } = renderHook(() => useCounter())
result.current.incrementAsync()
// 모든 타이머가 돌아간 후에 테스트 결과 확인
await act(() => jest.runAllTimers())
expect(result.current.count).toBe(1)
})
})
[참고]
패스트캠퍼스 강의 (100가지 시나리오로 학습하는 프론트엔드 : 5년 이상 경험을 초압축한 실전 문제 해결 패키지) 를 기반으로 작성하였고, 추가 서치 내용을 정리하였습니다.
'개발공부 > React' 카테고리의 다른 글
[React] CRA 없이 리액트 앱 만들기: 목적과 프로젝트 세팅 (4) | 2024.10.12 |
---|---|
[React] 테스팅 패턴 - 외부 모듈의 Mocking을 이용한 검증 (0) | 2024.07.31 |
[React] 테스팅 패턴 - 모의 데이터를 이용하여 검증 (0) | 2024.07.27 |
[React] 테스팅 패턴 - AAA 패턴 (0) | 2024.07.02 |
[React] 테스팅 패턴 - 설정과 해지 (0) | 2024.07.01 |