Queries[Testing Library]

SnowCat·2023년 3월 2일
0

Testing Library

목록 보기
1/11
post-thumbnail
import {render, screen} from '@testing-library/react'

test('should show login form', () => {
  render(<Login />)
  const input = screen.getByLabelText('Username')
  // input을 통한 행동
})
  • Testing Library에서 페이지 요소를 찾기 위해 Query 사용
  • Query를 받아 이벤트를 발생시키거나, jest를 사용해 element 테스트 가능

쿼리의 종류

  • getBy... => 쿼리에 일치하는 노드가 딱 하나만 있을때 이를 반환하고, 없거나 2개 이상이면 오류를 발생시킴
  • queryBy... => 쿼리에 일치하는 노드를 반환하고, 노드가 없으면 null을 반환하며, 2개 이상 존재하면 오류를 발생시킴
  • findBy... => 쿼리에 일치하는 element를 찾으면 객체가 담긴 프로미스를 반환함, 1초내에 element를 발견하지 못하거나 두개이상 찾으면 reject 반환
  • getAllBy... => 쿼리에 일치하는 노드 배열을 반환하고, 일치하는게 없으면 오류 발생
  • queryAllBy... => 쿼리에 일치하는 노드 배열을 반환하고, 일치하는게 없으면 빈 배열 반환
  • findAllBy... => 쿼리에 일치하는 객체 배열이 담긴 프로미스를 반환하며 1초 이내 찾지 못하면 reject됨

우선 순위

  • 테스트는 사용자가 상호작용하는 방법과 최대한 유사해야 하며, 이에 맞춰서 쿼리에도 우선순위를 고려할 필요가 있음

유저와 상호작용하는 요소에 사용하는 쿼리

  • getByRole : getByRole('button', {name: /submit/i})의 구문으로 사용하며, name 옵션을 사용해 accessible name에 접근 가능
    만약 getByRole로 접근이 불가능하면 UI에 접근 불가능할 가능성이 높음
  • getByLabelText : form 필드에서 사용자는 label을 통해 요소에 접근하기 때문에 form에서 우선적으로 사용
  • getByPlaceholderText: label이 없을 경우에 대안으로 사용
  • getByText : 다른 요소와 상호작용하지 않는 element(div, span 등등)에서 요소를 찾기 위한 방법
  • getByDisplayValue : form에서 값이 미리 채워진 요소들을 찾을 때 사용

시멘틱 태그에 사용하는 쿼리

  • getByAltText: 이미지처럼 alt 값이 있는 경우에 사용
  • getByTitle: 스크린리더가 항상 값을 불러오지 않음에 유의

테스트 ID

  • getByTestId: 사용자가 상호작용하는 것이 불가능하기 때문에 텍스트가 동적인 등의 특수한 상황에서만 사용할 것

쿼리의 사용

  • 쿼리로 dom을 가져오기 위해서는 첫 인자로 container를 가져옴
  • document.body 내부의 컨테이너를 가져오는 경우면 screen을 사용해 간단하게 테스트 가능
  • 쿼리식으로는 string, 정규식, 함수가 올 수 있음
  • 쿼리를 줄 때 queryOption을 더해줄 수 있음
// 예시 dom
<body>
  <div id="app">
    <label for="username-input">Username</label>
    <input id="username-input" />
  </div>
</body>

// 테스트 코드
import {screen, getByLabelText} from '@testing-library/dom'

// 렌더링 된 화면 내에서 사용하는 경우
const inputNode1 = screen.getByLabelText('Username')

// screen을 사용하지 않는 경우는 직접 인자 전달 (권장하지 않음)
const container = document.querySelector('#app')
const inputNode2 = getByLabelText(container, 'Username')

TextMatch

  • string, 정규식, 함수를 사용해 텍스트의 일치 여부를 확인하는 메서드
<div>Hello World</div>

// Matching a string:
screen.getByText('Hello World') // 값이 정확히 Hello World인가?
screen.getByText('llo Worl', {exact: false}) // 대소문자 상관없이 llo Worl 값이 포함되는가?
screen.getByText('hello world', {exact: false})

// 정규식 사용
screen.getByText(/World/) // World가 들어가있는가?
screen.getByText(/world/i) // (대소문자를 무시하고) world가 들어가있는가?
screen.getByText(/^hello world$/i) // 대소문자 상관없이 hello world가 정확히 일치하는가? (h로 시작, d로 끝나면서 값은 hello world 여야 함)
screen.getByText(/Hello W?oRlD/i) // 값에 대소문자 상관없이 Hello WoRld, Hello rRld가 들어가는가?

// 함수 사용
screen.getByText((content, element) => content.startsWith('Hello'))
  • 인자로 string이 들어오는 경우 exact 옵션을 줄 수 있음(기본값은 true)
    대소문자 구분을 하지 않고, 값이 포함되어있는지만 확인
    byRole 쿼리의 name옵션에는 영향을 주지 않음
    exact 옵션을 사용하지 말고 정규식을 사용하는 것을 권장
  • nomalizer 옵션에 getDefaultNormalizer을 호출해서 텍스트 표준화 방법을 조정할 수 있음
// trim: 앞뒤의 공백 제거
screen.getByText('text', {
  normalizer: getDefaultNormalizer({trim: false}),
})

// collapseWhitespace: 엔터, 탭, 반복되는 공백을 공백 하나로 변환
screen.getByText('text', {
  normalizer: str =>
    getDefaultNormalizer({collapseWhitespace: false})(str).replace(/[\u200E-\u200F]*/g, ''),
})

디버깅

  • 디버깅을 위해서 screen.debug(), screen.logTestingPlaygroundURL() 사용
import {screen} from '@testing-library/dom'

document.body.innerHTML = `
  <button>test</button>
  <span>multi-test</span>
  <div>multi-test</div>
`

// 출력된 document 전체 디버깅
screen.debug()
// test 버튼 하나를 디버깅
screen.debug(screen.getByText('test'))
// 여러개의 element를 디버깅
screen.debug(screen.getAllByText('multi-test'))

// playground에서 확인도 가능
screen.logTestingPlaygroundURL()
screen.logTestingPlaygroundURL(screen.getByText('test'))

querySelector()

  • querySelector를 사용해서도 값을 가져올 수 있지만 권장하지 않음
const {container} = render(<MyComponent />)
const foo = container.querySelector('[data-foo="bar"]')
profile
냐아아아아아아아아앙

0개의 댓글