오늘 읽은 범위
5장 UI 컴포넌트 테스트
책에서 기억하고 싶은 내용을 써보세요.
UI 컴포넌트 테스트
- UI 컴포넌트의 주요 기능들이 의도한 대로 작동하고 있는가, 문제가 생긴 부분이 없는 가를 확인한다.
- 데이터를 렌더링하는 기능
- 사용자의 입력을 전달하는 기능
- 웹 api와 연동하는 기능
- 데이터를 동적으로 변경하는 기능
- UI 컴포넌트를 통해 웹 접근성을 향상시킬 수 있다.
UI 컴포넌트 테스트의 기초 지식
render(): 테스트할 컴포넌트를 렌더링getByText(): 일치하는 문자열을 가진 한 개의 텍스트 요소를 찾는다.toBeInTheDocument(): 해당 요소가 DOM에 존재하는지 검증getByRole(): 특정 DOM요소를 role로 취득toHaveTextContent(): 문자가 포함되어있는지 검증fireEvent: 임의의 DOM 이벤트 발생
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Button from './Button';
test('버튼 클릭 시 텍스트 변경', () => {
render(<Button />);
// 초기 상태 검증
const btn = screen.getByRole('button');
expect(btn).toBeInTheDocument();
expect(btn).toHaveTextContent('클릭 전');
// 클릭 이벤트 발생
fireEvent.click(btn);
// 변경된 상태 검증
expect(screen.getByText('클릭 후')).toBeInTheDocument();
});아이템 목록 UI 컴포넌트 테스트
- li요소는 listitem이라는 role을 하기 때문에
getAllByRole('listitem')으로 모든 요소를 취득할 수 있다. →within()을 사용하여 취득할 노드의 범위를 줄일 수도 있다. - ul요소는
getByRole('list')로 취득할 수 있다. - 데이터가 없는 경우 목록을 표시하지 않고 ‘데이터 없음’이라고 표시하는 경우,
queryByRole()을 사용할 수 있다. queryBy 접두사가 붙은 api는 테스트가 에러 발생으로 중단되지 않는다.
NOTE
요소 취득 api는 원칙에 따라 다음과 같은 순서로 사용할 것을 권장
- 모두가 접근 가능한 쿼리 (웹 접근성 고려)
- getByRole, getByText, getByLabelText, getByPlaceholderText, getByDisplayValue
- 시맨틱 쿼리 → 브라우저나 보조 기기에 따라 다른 결과가 나올 수 있다.
- getByAltText, getByTitle
- 테스트 ID
- getByTextId
인터랙티브 UI 컴포넌트 테스트
- userEvent로 문자열 입력하기
- userEvent로 user 인스턴스를 생성
- user.type으로 입력을 재현
const user = userEvent.setup();
test('input에 문자열을 입력하면 값이 반영된다', async () => {
render(<Input placeholder="이름을 입력하세요" />);
const input = screen.getByPlaceholderText('이름을 입력하세요');
await user.type(input, '철수');
expect(screen.getByDisplayValue('철수')).toBeInTheDocument();
});- 버튼 활성화 여부 테스트 체크박스를 클릭하면 checked상태가 변경되면서 회원가입 버튼이 활성화 되는 UI가 있다.
test('체크박스 클릭 시 회원가입 버튼 활성화', async () => {
const user = userEvent.setup();
render(<SignUpForm />);
const checkbox = screen.getByRole('checkbox', { name: /약관 동의/i });
const submitButton = screen.getByRole('button', { name: /회원가입/i });
// 초기 상태: 버튼 비활성화
expect(submitButton).toBeDisabled();
// 체크박스 클릭
await user.click(checkbox);
// 체크 후: 버튼 활성화
expect(submitButton).toBeEnabled();
});유틸리티 함수를 활용한 테스트
화면 분기가 있는 경우, 여러번 동일한 인터랙션을 작성해야할 때가 많다. 이때, 하나의 함수로 정리하면 재사용이 가능하다.
/** 약관 동의 후 회원가입 버튼 클릭 시퀀스 */
export async function agreeAndSubmit() {
const user = userEvent.setup();
const checkbox = screen.getByRole('checkbox', { name: /약관 동의/i });
const submitButton = screen.getByRole('button', { name: /회원가입/i });
await user.click(checkbox);
await user.click(submitButton);
}스냅샷 테스트
- UI 컴포넌트가 예기치 않게 변경됐는지 검증하고 싶을 때 사용한다.
- 테스트를 실행하면 변경된 부분에 diff가 생긴다.
- 실패한 테스트를 성공시키려면 커밋된 스냅샷을 갱신해야한다.
test('snapshot: 계정명이 표시됐는지 확인한다", () => {
const { container } = render(<Form name="철수"/>);
expect(container).toMatchSnapshot();
})암묵적 역할과 접근 가능한 이름
- 암묵적 역할 : 명시적으로 role을 지정하지 않아도 초깃값으로 부여된 역할
- 역할과 요소는 1:1로 매칭되지 않는다.
- 접근 가능한 이름 : 보조 기기가 인식하는 노드의 명칭
