import {render, screen} from '@testing-library/react'
test('should show login form', () => {
render(<Login />)
const input = screen.getByLabelText('Username')
})
- 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을 더해줄 수 있음
<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')
const container = document.querySelector('#app')
const inputNode2 = getByLabelText(container, 'Username')
TextMatch
- string, 정규식, 함수를 사용해 텍스트의 일치 여부를 확인하는 메서드
<div>Hello World</div>
screen.getByText('Hello World')
screen.getByText('llo Worl', {exact: false})
screen.getByText('hello world', {exact: false})
screen.getByText(/World/)
screen.getByText(/world/i)
screen.getByText(/^hello world$/i)
screen.getByText(/Hello W?oRlD/i)
screen.getByText((content, element) => content.startsWith('Hello'))
- 인자로 string이 들어오는 경우 exact 옵션을 줄 수 있음(기본값은 true)
대소문자 구분을 하지 않고, 값이 포함되어있는지만 확인
byRole 쿼리의 name옵션에는 영향을 주지 않음
exact 옵션을 사용하지 말고 정규식을 사용하는 것을 권장
- nomalizer 옵션에 getDefaultNormalizer을 호출해서 텍스트 표준화 방법을 조정할 수 있음
screen.getByText('text', {
normalizer: getDefaultNormalizer({trim: false}),
})
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>
`
screen.debug()
screen.debug(screen.getByText('test'))
screen.debug(screen.getAllByText('multi-test'))
screen.logTestingPlaygroundURL()
screen.logTestingPlaygroundURL(screen.getByText('test'))
querySelector()
- querySelector를 사용해서도 값을 가져올 수 있지만 권장하지 않음
const {container} = render(<MyComponent />)
const foo = container.querySelector('[data-foo="bar"]')