TLDR
- 구현 세부사항과 관련없는 테스트
- 사용자가 보는것처럼 테스트
- 마크업과 관련없는 유즈케이스 테스트
- 스크린 유틸리티
- 인터랙션 > 유저이벤트
- 눈에 보이는 데이터 중 핵심에 초점
이전 포스트에서 사용자가 사용하는 방법
(렌더링 결과와 프롭) => (인터랙션) => (렌더링 결과와 프롭)
측면에서 테스트해야 함을 이해했습니다.
UI 측면의 테스트는 사용자가 보고, 듣고, 느끼는 것에 관한 것입니다.
implementation details
const multiply = (a, b) => a * b
// 는 아래와 같습니다.
function multiply(a, b) {
let total = 0
for (let i = 0; i < b; i++) {
total += a
}
return total
}
리팩터링을 통해 계속 동작한다는 확신 관점에서, 테스트도 마찬가지입니다.
리팩터링을 수행하는 개발자도 테스트가 어떻게 짜여져 있는지 관심이 없습니다.
구현 세부 사항은 (UI, API) 사용자가 신경 쓰지 않는 것입니다.
리액트 테스트 예제
아래와 같은 카운터 컴포넌트가 존재합니다.
function Counter() {
const [count, setCount] = React.useState(0)
const increment = () => setCount(c => c + 1)
return <button onClick={increment}>{count}</button>
}
테스트 코드에서 버튼을 아래와 같이 접근할 수 있습니다.
const {container} = render(<Counter />)
container.firstChild // <-- that's the button
UI 개발자와 UI 사용자는 마크업에 관심이 없습니다. 오직 버튼에만 관심이 있습니다
아래와 같이 마크업을 바꾸었다고 테스트가 깨진다면, 이상할 겁니다.
function Counter() {
const [count, setCount] = React.useState(0)
const increment = () => setCount(c => c + 1)
return (
<span>
<button onClick={increment}>{count}</button>
</span>
)
}
그럼 어떻게 테스트해야 할까요?
사용자가 화면을 보는 것처럼 테스트합니다.
- 사용자는 버튼을 찾습니다.
- 버튼의 초기값은 항상 0입니다.
- 해당 버튼을 클릭합니다.
render(<Counter />)
screen.getByText('0') // <-- that's the button
// or (even better) you can do this:
screen.getByRole('button', {name: '0'}) // <-- that's the button
즉, useCase(feature)와 관련된 부분을 테스트하면 됩니다.
즉 시스템 조작과 시스템 동작 결과(리액트의 경우 상태 반영 결과 => 렌더링 결과)입니다.
이전 게시물에서 작업한 내용에서 구현 세부사항을 걷어내 봅시다.
Screen Utility 활용
https://testing-library.com/docs/queries/about/#priority 문서를 참고하면, selector의 priority를 설명한 내용이 있습니다.
이 중 가장 우선하는 내용은, 눈에 보이는 것중에서도 Role 입니다.
import * as React from 'react'
import {render, screen, fireEvent} from '@testing-library/react'
import Counter from '../../components/counter'
test('counter increments and decrements when the buttons are clicked', () => {
render(<Counter />)
const increment = screen.getByRole('button', {name: /increment/i})
const decrement = screen.getByRole('button', {name: /decrement/i})
const message = screen.getByText(/current count/i)
expect(message).toHaveTextContent('Current count: 0')
fireEvent.click(increment)
expect(message).toHaveTextContent('Current count: 1')
fireEvent.click(decrement)
expect(message).toHaveTextContent('Current count: 0')
})
브라우저 인터랙션 => UserEvent로 교체
import * as React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Counter from '../../components/counter'
test('counter increments and decrements when the buttons are clicked', () => {
render(<Counter />)
const increment = screen.getByRole('button', {name: /increment/i})
const decrement = screen.getByRole('button', {name: /decrement/i})
const message = screen.getByText(/current count/i)
expect(message).toHaveTextContent('Current count: 0')
userEvent.click(increment)
expect(message).toHaveTextContent('Current count: 1')
userEvent.click(decrement)
expect(message).toHaveTextContent('Current count: 0')
})
더 나아가서
Current count : 숫자에서
더 중요한 메세지는 숫자일 것입니다.
카운트 : 0이건
ㅇㅁㄴㅇㅁㄴ : 0이건
사용자는 0에 더 관심을 둘것입니다.
소프트웨어를 사용하는 사람 입장에서 변하지 않는것(불변식)을 테스트 합시다.
더 공부하기
https://testing-library.com/docs/dom-testing-library/api-queries#screen
Testing Implementation Details (kentcdodds.com)
About Queries | Testing Library (testing-library.com)
Avoid the Test User (kentcdodds.com)
'FrontEnd' 카테고리의 다른 글
리액트 라우터 v6를 이용해 쉽게 모달 만들기 (0) | 2022.06.27 |
---|---|
리액트 테스트 : 폼(Form) 테스트 (0) | 2022.06.26 |
리액트 테스트 : React Testing Library로 Counter 테스트하기 (0) | 2022.06.26 |
리액트 테스트 : 라이브러리 없이 Counter 테스트하기 (0) | 2022.06.26 |
CSS 애니메이션 : 접근성 (0) | 2022.06.26 |