[React] 테스팅 심화

정호·2026년 4월 14일

React

목록 보기
16/30

리액트 테스팅 완벽 가이드: 기초부터 비동기 Mocking까지

"사용자가 소프트웨어를 사용하는 방식과 유사하게 테스트하라"는 철학 아래, 컴포넌트의 렌더링부터 복잡한 비동기 로직까지 검증하는 실전 테스팅 기법임

키워드: RTL(React Testing Library), AAA 패턴, User Event, Mocking(jest.fn), Async Testing


1. 리액트 테스팅의 핵심 철학

리액트 테스팅 라이브러리(RTL)의 최우선 가치는 내부 구현(State, Props 명칭 등)이 아닌 사용자의 관점을 검증하는 것입니다.

핵심 원칙:

  • 내부 상태값이 무엇인지보다 화면에 무엇이 보이고, 어떤 상호작용이 일어나는지가 중요함.
  • 코드를 리팩토링해도 사용자 경험이 같다면 테스트는 통과해야 함.

2. 테스트의 기본 구조: AAA 패턴

모든 테스트 코드는 가독성과 유지보수를 위해 3단계 구조를 따릅니다.

  1. Arrange (준비): 테스트할 컴포넌트를 render()로 가상 DOM에 띄움.
  2. Act (실행): 클릭, 타이핑 등 사용자의 행동을 수행 (userEvent 활용).
  3. Assert (단언): 결과가 예상과 일치하는지 expect()로 확인.

3. 단계별 실습 및 메서드 분석

① 기초: 정적 컴포넌트 테스트 (getByText)

가장 기본적인 텍스트 렌더링 확인 단계입니다. describe로 관련 테스트를 그룹화합니다.

describe('Greeting component', () => {
  test('renders "Hello World" as a text', () => {
    render(<Greeting />); // Arrange
    const helloWorldElement = screen.getByText('Hello World!'); // Act & Assert
    expect(helloWorldElement).toBeInTheDocument();
  });
});

② 중급: 이벤트와 상태 변경 (getByRole & userEvent)

버튼 클릭 시 UI가 변하는지 테스트합니다. userEvent는 실제 브라우저의 상호작용(포커스, 클릭 등)을 더 정밀하게 흉내 냅니다.

test('버튼 클릭 시 "Changed!" 문구가 나타나는지 확인', () => {
  render(<Greeting />);
  
  // 1. 역할(Role)로 버튼 찾기
  const buttonElement = screen.getByRole('button');
  
  // 2. 사용자의 실제 클릭 동작 수행
  userEvent.click(buttonElement); 

  // 3. 변경된 UI 검증
  const outputElement = screen.getByText('Changed!');
  expect(outputElement).toBeInTheDocument();
});

③ 고급: 요소의 부재 확인 (queryBy)

특정 요소가 화면에 없어야 함을 검증할 때는 getBy를 쓰면 안 됩니다. 요소를 찾지 못하는 순간 에러를 던져 테스트가 중단되기 때문입니다.

  • queryByText: 요소를 찾지 못하면 에러 대신 null을 반환합니다.
  • 활용: expect(...).toBeNull()과 함께 사용하여 특정 UI가 사라졌음을 안전하게 확인합니다.

④ 심화: 비동기 데이터와 모킹 (Mocking)

API 호출처럼 시간이 걸리는 작업은 실제 네트워크를 타지 않도록 jest.fn()으로 가짜 함수를 만들어 대체(Mocking)합니다.

test('성공 시 포스트 목록을 렌더링함', async () => {
  // 1. Mocking: 실제 fetch를 가짜 함수로 대체하여 외부 의존성 제거
  window.fetch = jest.fn();
  window.fetch.mockResolvedValueOnce({
    json: async () => [{ id: 'p1', title: 'First post' }],
  });

  render(<Async />);

  // 2. 비동기 대기: findAllByRole
  // find로 시작하는 메서드는 요소가 나타날 때까지 기다리며 Promise를 반환함
  const listItemElements = await screen.findAllByRole('listitem');

  // 3. 결과 검증
  expect(listItemElements).not.toHaveLength(0);
});

### 테스팅 메서드 선정 가이드

상황에 맞는 쿼리(Query)를 선택하는 것이 테스트의 견고함을 결정합니다. 단순히 요소를 찾는 것을 넘어, 테스트의 의도에 맞는 접두사를 사용하는 것이 중요합니다.

메서드 접두사특징용도
getBy...요소를 즉시 찾음. 없으면 즉시 에러 발생일반적인 요소 존재 확인 (가장 기본적이고 많이 쓰임)
queryBy...요소를 찾지 못하면 에러 대신 null 반환특정 요소가 존재하지 않음을 안전하게 확인할 때
findBy...요소를 찾을 때까지 기다림 (Promise 반환)비동기 데이터 로딩 후 화면에 나타나는 요소를 확인할 때

💡 쿼리 선택의 팁

  1. 우선순위: 가능하면 getByRole을 최우선으로 사용하세요. 이는 시각 장애인이 스크린 리더를 사용하는 방식과 유사하여 접근성까지 함께 테스트할 수 있습니다.
  2. 에러 메시지: getBy는 요소를 찾지 못할 때 전체 DOM 구조를 에러 메시지로 보여주기 때문에 디버깅에 매우 유리합니다.
  3. 비동기 처리: findBy는 기본적으로 1000ms(1초) 동안 요소를 기다립니다. API 응답 속도에 따라 이 시간은 조절 가능합니다.
profile
열심히 기록할 예정🙃

0개의 댓글