컴포넌트 요소 선택

React 컴포넌트를 렌더링 한 후, RTL는 다양한 검색 기능을 제공하여 요소를 가져오는 명령을 실행합니다. 가저온 요소는 어설션(assertion) 테스트에 사용되거나, 사용자와의 인터랙션(interaction) 테스트에 사용됩니다.

getByText

import { render, screen } from '@testing-library/react'
import App from './App'

describe('App 컴포넌트', () => {
  test('React 앱 렌더링 학습 링크 포함 여부 확인', () => {
    render(<App />)
    
    // screen의 getByText()를 사용해 매칭되는 텍스트 값으로 대상(요소)을 찾을 수 있습니다.
    const linkElement = screen.getByText(/React를 배워보세요/i)
    
    expect(linkElement).toBeInTheDocument()
  })
})

컴포넌트 랜더링 결과를 유추하기 어려운 경우, screen.debug()를 사용해 터미널에서 렌더링 결과를 확인합니다.

커스텀 매처를 사용해 특정 텍스트를 가진 요소가 문서에 있는지 검사 할 수 있습니다.

test('"React 테스팅 라이브러리" 문구를 포함합니다', () => {
  render(<App />);
  
  // 특정 텍스트를 가진 요소가 문서에 포함되어 있는 지 검사합니다.
  expect(screen.getByText('React 테스팅 라이브러리')).toBeInTheDocument()
})

문자 값을 정확히 특정하기 어려운 경우, 정규 표현식을 사용해 검사 할 수도 있습니다.

test('App 컴포넌트 렌더링', () => {
  render(<App />);

  // 테스트 예시: ' 테스트 '

  // 실패
  expect(screen.getByText('테스팅')).toBeInTheDocument()

  // 실패
  expect(screen.getByText(' 테스팅')).toBeInTheDocument()

  // 성공!
  expect(screen.getByText(/\s?테스팅\s?/)).toBeInTheDocument()
})

getByRole

getByRole() 함수는 일반적으로 WAI-ARIA aria-label, role 속성을 가진 요소를 검색하는 데 사용됩니다. RTL은 시각적으로 표시되는 텍스트 외에도 접근성 역할(Role)을 통해 대상(요소)를 선택할 수 있습니다. getByText()와 함께 RTL에서 널리 사용되는 검색 함수입니다.

test('img 요소를 포함하는 지 검사합니다.', () => {
  render(<App />)

  // 이미지(img) 역할을 가진 요소가 문서에 포함되어 있는 지 검사합니다.
  expect(screen.getByRole('img')).toBeInTheDocument()
})

다른 검색 함수

이 외에도 RTL은 요소 별로 검색 가능한 다양한 함수를 제공합니다.

검색

함수

예시 코드

레이블 텍스트

<label htmlFor="검색" />

플레이스홀더 텍스트

<input placeholder="검색어를 입력하세요." />

대체 텍스트

<img alt="교통 안내를 하는 포돌이" />

디스플레이 값

<input value="React 테스팅 라이브러리" />

변형 검색 함수

getBy*() 함수 외에도, 변형 된 queryBy*(), findBy*() 함수를 사용해 대상(요소)을 찾을 수 있습니다.

queryBy

  • queryByText()

  • queryByRole()

  • queryByLabelText()

  • queryByPlaceholderText()

  • queryByAltText()

  • queryByDisplayValue()

findBy

  • findByText()

  • findByRole()

  • findByLabelText()

  • findByPlaceholderText()

  • findByAltText()

  • findByDisplayValue()

getBy VS queryBy 비교

getBy로 존재하지 않는 대상을 검색할 때 다음과 같은 오류가 출력됩니다. 오류 메시지를 살펴보면 검색한 텍스트가 여러 요소로 구분되어 있어 문제가 발생한 오류임을 안내합니다.

test('"React 테스팅 라이브러리" 문구를 포함합니다', () => {
  render(<App />)

  // TestingLibraryElementError 오류 출력 ✔︎
  expect(screen.getByText(/React 테스팅 라이브러리/)).toBeNull()
})

이런 경우 getByText 대신, queryByText를 사용하면 존재하지 않는 대상을 검색해도 오류가 발생하지 않습니다.

test('"React 테스팅 라이브러리" 문구를 포함합니다', () => {
  render(<App />)

  // TestingLibraryElementError 오류 출력 ✘
  expect(screen.queryByText(/React 테스팅 라이브러리/)).toBeNull()
})

toBeNull()은 toBe(null)과 동일하지만, 출력되는 오류 메시지가 좀 더 명확합니다. 무언가가 null 인지 확인하고 싶을 때 사용합니다.

findBy는 언제 사용할까?

findByText()는 비동기(async) 처리 과정에 사용합니다. 아례 예를 살펴보면 컴포넌트에서 초기 렌더링 시점이 아닌, 상태 변경에 따른 업데이트 시점에 문서의 대상을 찾아야 할 때 findByText() 검색 함수를 사용합니다.

// 비동기(async) 콜백 함수
test('UserSearch 테스트', async () => {

  render(<UserSearch />)

  // FAIL 실패! null 반환
  expect(screen.queryByText(/로그인 사용자:/)).toBeInTheDocument()
  
  // 스크린 디버깅 (초기 렌더링 된 UI)
  screen.debug()

  // PASS 통과! await 비동기 처리 후, 업데이트 된 UI에서 대상(요소)를 찾음
  expect(await screen.findByText(/로그인 사용자:/)).toBeInTheDocument();

  // 스크린 디버깅 (업데이트 된 UI)
  screen.debug(); 

})
/**
 * 사용자 정보 가져오기 함수
 */
function getUser() {
  // Promise 객체 반환
  return Promise.resolve({ id: 'pzke1', name: '야무' })
}

/**
 * Search 컴포넌트
 */
function Search({ value, onChange, children }) {
  return (
    <div>
      <label htmlFor="search">{children}</label>
      <input
        id="search"
        type="text"
        value={value}
        onChange={onChange}
      />
    </div>
  )
}

/**
 * UserSearch 컴포넌트
 */
class UserSearch extends React.Component {
  state = {
    search: '',
    user: null,
  }

  loadUser = async () => {
    const user = await getUser();
    this.setState({ user });
  }

  handleChange = (event) => {
    this.setState({
      search: event.target.value,
    })
  }

  componentDidMount() {
    this.loadUser();
  }

  render() {
    const { search, user } = this.state;

    return (
      <div>
        {user ? <p>로그인 사용자: {user.name}</p> : null}

        <Search value={search} onChange={this.handleChange}>검색</Search>

        <p>검색어: {search ? search : '...'}</p>
      </div>
    )
  }
}

export default UserSearch

복수 대상 찾기

앞서 getBy, queryBy, findBy 검색 함수를 살펴봤습니다. 3가지 유형의 함수 모두 단수(1개) 대상을 검색해 찾을 때 사용합니다. 만약 복수(2개 이상)의 대상을 검색해 찾고자 한다면? getAllBy, queryAllBy, findAllBy 검색 함수를 활용합니다. All이 들어간 검색 함수는 대상 집합(배열)을 반환합니다.

Last updated

Was this helpful?