이번 포스팅은 여태 했던 포스팅과는 달리 React Testing Library와 jest, vitest에 대해 학습하며 얻은 지식들을
모두 한 곳에 모아서 포스팅 할 예정이라 학습에 대한 순서가 조금 뒤죽박죽일 수는 있다.
바로 시작해보자 !
❗️ getByText()와 queryByText()의 차이
React Testing Library를 사용한 테스트 맥락에서 'getByText' 및 'queryByText'는 모두 React 구성 요소에서 렌더링된 요소를 쿼리하고 상호 작용하는 데 사용되는 메서드이다. 그러나 요소의 유무를 처리하는 방법에는 차이점이 있다.
getByText와 queryByText는 텍스트 내용을 기반으로 요소를 찾는 데 사용되는 공통점을 가지고 있지만,
getByText는 정의한 요소가 문서에 있을 것으로 예상하여 지정된 텍스트 콘텐츠가 포함된 요소를 찾을 수 없는 경우 테스트 실패를 나타내는 오류를 발생시키는 반면,
queryByText는 정의한 요소를 찾을 수 없어도 오류를 발생시키지 않고 null을 반환하여 테스트에서 요소가 없는 경우를 처리할 수 있습니다.
각 queries별 반환값에 대해서는 해당 이미지를 참고하자
❗️ fireEvent와 userEvent에 대해 알아보자
✅ fireEvent
React Testing Library에서 제공하는 유틸리티 함수 중 하나로 테스트 중에 이벤트를 발생시키는 데 사용한다. React 컴포넌트를 테스트할 때 사용자 동작을 모의하고 특정 이벤트를 발생시켜 컴포넌트의 상태 변화 및 렌더링을 확인하는 데 유용하다.
react-testing-library에서 단순히 처음에 보여지는 부분들에 대해서 테스트할 뿐 아니라 컴포넌트에서 사용자 상호작용 및 이벤트가 발생했을 때에 어떤 변화가 있는지 검증하기 위해 fireEvent가 필요하다.
click(), mouseover(), focus(), change()등 이벤트를 트리거할 수 있는 다양한 메소드들을 제공하고 테스트 코드에서 적절한 엘리먼트에 이벤트를 fire 하면 이벤트가 발생하고 실제 컴포넌트에서 작성된 이벤트에 대한 변화를 확인할 수 있다.
✅ userEvent
userEvent는 fireEvent를 사용해서 만들어졌다.
fireEvent는 어떠한 element 타입이든 똑같은 반응을 보여주지만 userEvent는 각 element 타입에 맞는 더욱 적절한 반응을 보여준다.
userEvent는 사용자가 애플리케이션과 상호 작용하는 방식을 복제하여 보다 사용자 중심적으로 만드는 것을 목표로 하고, 사용자 행동과 매우 유사한 방식으로 입력, 클릭, 마우스 오버 등과 같은 이벤트를 시뮬레이션한다.
⭐️⭐️ 결론: 실제 사용하는 유저가 보기에 실제 버튼을 클릭하는 행위가 더 잘 표현되고 테스트를 할 때 실제 유저가 사용하듯이 테스트해야하기 때문 fireEvent보단 userEvent를 사용하는 게 좋다.
참고 : https://testing-library.com/docs/user-event/intro/
❗️findAllByRole와 getAllByRole에 대해서 알아보자
✅ findAllByRole
렌더링된 컴포넌트 내에서 지정된 역할과 일치하는 모든 요소를 찾고, 일치하는 요소의 배열을 반환한다.
✅ getAllByRole
렌더링된 컴포넌트 내에서 지정된 역할과 일치하는 모든 요소를 찾는다.
findAllByRole과 달리 해당 역할에 일치하는 요소가 없거나 하나 이상인 경우 에러를 throw한다.
비동기 코드를 테스트 하기 위해서는, getAllByRole이 아닌 findAllByRole을 사용 해야한다.
findAllByRole은 find 쿼리로 promise를 반환한다는 특징이 있다.
React Testing Library는 이 과정이 성공할 때까지 screen을 여러 번 재평가 하게 돠는데, findAllByRole의 timeout 옵션을 주어 기다리는 시간을 설정할 수도 있다. (기본값은 1초)
const scoopImages = await screen.findAllByRole("img", { name: /scoop$/i });
expect(scoopImages).toHaveLength(2);
그렇기 때문에 이렇게 findAllByRole을 쓸 때는 앞에 await를 붙혀줘야한다.
❗️get, query, find에 대해서 알아보자
✅ get
- 구성요소의 렌더링된 출력에 나타날 것으로 예상되는 요소를 동기적으로 검색하는 데 사용된다.
- 요소를 찾을 수 없으면 오류가 발생하여 테스트가 실패한다
- 구성 요소에 요소가 있을 것으로 예상하고 해당 요소를 찾을 수 없는 경우 테스트가 실패하도록 할 때 유용하게 쓰인다.
✅ query
- 구성요소의 렌더링된 출력에 존재할 수도 있고 존재하지 않을 수도 있는 요소를 동기적으로 검색하는 데 사용된다.
- 요소가 발견되면 해당 요소를 반환하고, 그렇지 않으면 null을 반환한다
- 요소를 찾을 수 없는 경우 테스트에 실패하지 않고 요소의 존재 여부를 확인하고 싶을 때 유용하게 쓰인다
✅ find
- 약간의 지연 또는 비동기 작업 후에 구성 요소의 렌더링된 출력에 나타날 것으로 예상되는 요소를 비동기적으로 검색하는 데 사용된다.
- 이러한 메서드는 요소가 발견되면 확인하거나 시간 초과가 발생하면 거부하는 Promise를 반환합니다
- API에서 데이터를 가져오거나 상태를 업데이트하는 등 비동기 작업 후에 구성 요소의 렌더링된 출력에 요소가 나타날 것으로 예상되는 경우 유용하게 쓰인다
❗️특정 파일 및 특정 테스트만 테스트하기
✅ 특정 파일만 테스트 하기
터미널에서 h를 입력한 뒤
p를 입력하고 Input filename pattern을 입력하면 입력한 파일명에 해당하는 파일만을 테스트 하게된다.
✅ 특정 테스트만 테스트 하기
test("my test 1", () => {}); // normal
test.only("my test 2", () => {}); // 해당 테스트만 실행
test.skip("my test 1", () => {}); // 해당 테스트는 건너뛰기
참 쉽죠잉 ?
❗️logRoles
logRoles는 개발자가 렌더링된 구성 요소 내의 요소에 할당된 접근성 역할을 디버그하고 이해할 수 있도록 돕기 위해 제공되는 기능이다. 이 함수는 테스트 라이브러리 유틸리티의 일부이며 테스트에서 특정 쿼리와 일치하는 요소의 접근성 역할을 기록하는 데 사용할 수 있다.
React 구성 요소의 접근성 문제를 디버깅하고 구성 요소의 구조와 렌더링 방법을 이해하는 데 아주 유용하게 사용된다.
하지만 일반적으로 프로덕션 코드에는 'logRoles'를 포함하지 않는다.
import { render, screen, logRoles } from "@testing-library/react";
test("handles error for scoops and toppings routes", async () => {
const { container } = render(<OrderEntry />);
render(<OrderEntry />);
const alerts = await screen.findAllByRole("alert");
logRoles(container); //요소들이 페이지에 렌더링 된 다음 부분에 사용
});
이렇게 사용하면
이렇게 유용한 결과를 얻을 수 있다.
❗️userEvent type과 clear
✅ type
userEvent.type 함수는 입력 요소에 텍스트를 입력하거나 사용자가 키를 입력하는 것과 유사한 작업을 수행한다.
이 함수는 입력 요소에 대한 포커스를 설정하고, 각 문자 또는 키를 연속적으로 입력하며, 입력 내용이 변경될 때 이벤트를 트리거한다.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('typing text into input field', () => {
render(<input type="text" />);
const inputField = screen.getByRole('textbox');
userEvent.type(inputField, 'Hello, world!');
expect(inputField).toHaveValue('Hello, world!');
});
userEvent.type 함수를 사용하여 텍스트 입력 필드에 'Hello, world!'를 입력하는 예시코드이다.
✅ clear
userEvent.clear 함수는 입력 요소의 내용을 지우는 작업을 수행한다.
이 함수는 입력 요소에 대한 포커스를 설정하고, 입력 요소의 내용을 삭제한다.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('clearing text from input field', () => {
render(<input type="text" value="Hello, world!" />);
const inputField = screen.getByRole('textbox');
userEvent.clear(inputField);
expect(inputField).toHaveValue('');
});
userEvent.clear 함수를 사용하여 텍스트 입력 필드의 내용을 지우는 예시이다.
// 내용은 지속적으로 추가 예정