렌더링 결과에 조금 더 집중, DOM 위주로 진행
-> 실제 DOM에 대해서 신경을 더 많이 쓰고, 컴포넌트의 인스턴스에 대해서 신경쓰지 않고, 실제 화면에 무엇이 보여지는지, 그리고 어떠한 이벤트가 발생했을 떄 화면에 원하는 변화가 생겼는지 확인하는 것에 조금 더 최적화(컴포넌트의 props나 state를 조회하는 일이 X)
사용자 관점에서 테스팅하기가 더욱 용이
컴포넌트를 리팩토링할 때 주로 내부 구조 및 네이밍은 많이 바뀌어도 실제 작동 방식은 크게 변경 X
-> 따라서 react-testing-library가 이 점을 유의하여, 컴포넌트의 기능이 똑같이 작동한다면 컴포넌트의 내부 구현 방식이 만히 바뀌어도 테스트가 실패하지 않도록 설계
Enzyme에 비해 정말 필요한 기능들만 지원해주어 가볍고, 개발자들이 일관성 있고 좋은 관습을 따르는 테스트 코드를 작성할 수 있게 유도
(Enzyme:구현 주도 테스트 , React Testing Library: 행위 주도 테스트)
Create React App으로 생성된 프로젝트는 즉시 React Testing Library를 지원. 그렇지 않은 경우 다음과 같이 npm을 통 해 추가 가능
DOM testing Library 위에 구축되어 있는 라이브러리
💡중요!
DOM(Document Object Model)
- Document: 문서 / Object: 객체 / Model: 모델
- DOM은 XML, HTML 문서의 각 항목을 계층으로 표현하여 생성,변형 삭제할 수 있도록 돕는 인터페이스이다. (:위키피디아)
" HTML 요소들의 구조화된 표현이며,DOM은 HTML이 브라우저의 렌더링 엔진에 의해 분석되고 분석이 모두 끝나고난 HTMl 파일이 DOM이다.HTML은 화면에 보이고자 하는 모양과 구조를 문서로 만들어서 단순 텍스트로 구성되어있으며 DOM은 HTML문서의 내용과 구 조가 객체 모델로 변화되어 다양한 프로그램에서 사용될 수 다. HTML 문서가 유효하지 않게 작성됐을때는 브라우저가 올바르 게 교정해주며, DOM은 자바스크립트에 의해 수정될 수 있다. 하지만 HTML은 수정하지 않는다."웹 페이지 빌드 과정(Critical Rendering Path CRP)
- 브라우저가 서버에서 페이지에 대한 HTML 응답을 받고 화면에 표시하기 전에 여러 단계가 있다.
웹 브라우저가 HTML 문서를 읽고, 스타일 입히고 뷰포트에 표시하 는 과정(CRP 과정)1. (HTML, CSS) + javascript
-> 문서를 읽어들여서 그것들을 파싱하 고 어떤 내용을 페이지에 렌더링할 지 결정.2. Render Tree
->브라우저가 DOM과 CSSOM을 결합하는 곳이며, 이 프로 세스는 화면에 보이는 모든 콘텐츠의 콘텐츠와 스타일 정보를 모두 포함하 는 최종 렌더링 트리를 출력. 즉 화면에 표시되는 모든 노드의 콘텐 츠 및 스타일 정보를 포함.3. Layout
-> 브라우저가 페 이지에 표시되는 각 요소 의 크기와 위치를 계산하는 단계4. Paint
-> 페인트 단계에 도달하면 브라우저는 레이아웃 결 과를 선택하고 픽셀을 화 면에 페인트해야함.
😙 잠깐)
RTL을 처음 접할 때면 RTL을 Jest의 대안으로 혼동하는 경우가 있다. 두 도구는 React 내에서 테스트를 진행할 때 같이 사용되기에 상호 보완 관계라고 볼 수 있다. (엄밀히 말하자면, RTL이 Jest를 포함하는 구조.) 전반적으로 Jest를 통해 기능 테스트를 진행할 수는 있지만, React의 컴포넌트를 렌더링하고 테스트하기 위해서는 몇가지 기능이 더 필요하기 때문.
- Jest - 자체적인 test runner와 test util 제공
- RTL - Jest + React 컴포넌트 test util 제공
그렇기에 리액트 내부의 테스트 코드 작성 방식은 Jest의 방식과 상당 부분 유사.출처: https://tecoble.techcourse.co.kr/post/2021-10-22-react-testing-library/
1. CRA 생성
$ yarn create rtl-tutorial
npx create-react-app rtl-tutorial
2. 라이브러리 설치
npm install --save-dev @testing-library/react
npm install --save-dev @testing-library/jest-dom
3. EsLint 설정
<1.> Create React App 으로 리액트를 설치할 때 기본 eslint가 설정
<2.> eslint 설정 파일 생성
<3.> ESLint Testing Plugins 설치
npm install eslint-plugin-testing-library eslint-plugin-jest-dom
잠깐) Plugins?
- eslint에서 기본으로 제공하지 않는 다양한 규칙을 플러 그인을 통해 사용하기 가능.
ex) react에 관련된 린트설정을 위해서는 eslint- plugin-react를 사용하면 되며, react hooks에 관련된 규칙을 적용시켜주려면 eslint-plugin-react-hooks를 사용하면 됩니다.
<4.> 내부 설정해주기
<5.> lint 작동확인
4. Prettier 설정
설치
1. Profile.js & App.js
<Profile.js>
import React from 'react';
const Profile = ({ username, name }) => {
return (
<div>
<b>{username}</b>
<span>({name})</span>
</div>
);
};
export default Profile;
<App.js>
import React from 'react';
import Profile from './Profile';
const App = () => {
return <Profile username="velopert" name="김민준" />;
};
export default App;
2. Profile.test.js
- 컴포넌트를 렌더링할 때는 render()함수를 사용
-> 이 함수가 호출되면 그 결과물에는 DOM을 선택할 수 있는 다양한 쿼리들과 container가 포함(container는 해당 컴포넌트의 최상위 DOM을 가리킨다. 이를 가지고 스냅샷 테스팅 가능)
import React from 'react';
import { render } from 'react-testing-library';
import Profile from './Profile';
describe('<Profile />', () => {
it('matches snapshot', () => {
// Profile컴포넌트 렌더링하는 utils변수 생성
const utils = render(<Profile username="houya" name="박정호" />);
expect(utils.container).toMatchSnapshot(); //최상위 DOM을 기준으로 스냅샷 생성
});
it('shows the props correctly', () => {
const utils = render(<Profile username="houya" name="박정호" />);
utils.getByText('houya'); // houya 라는 텍스트를 가진 엘리먼트가 있는지 확인
utils.getByText('(박정호)'); // (박정호) 이라는 텍스트를 가진 엘리먼트가 있는지 확인
utils.getByText(/박/); // 정규식 /박/ 을 통과하는 엘리먼트가 있는지 확인
});
});
3. 스냅샷 테스팅 생성
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Profile /> matches snapshot 1`] = `
<div>
<div>
<b>
houya
</b>
<span>
(
박정호
)
</span>
</div>
</div>
`;
<label for="username-input">아이디</label>
<input id="username-input" />
const inputNode = getByLabelText('아이디');
<input placeholder="아이디" />;
const inputNode = getByPlaceholderText('아이디');
<div>Hello World!</div>;
const div = getByText('Hello World!');
const div = getByText(/^Hello/);
<img src="/awesome.png" alt="awesome image" />;
const imgAwesome = getByAltText('awesomse image');
ByTitle 은 title 속성을 가지고 있는 DOM 혹은 title 엘리먼트를 지니고있는 SVG 를 선택 할 때 사용.
title 속성은 html 에서 툴팁을 보여줘야 하는 상황에 사용하곤 함.
<p>
<span title="React">리액트</span>는 짱 멋진 라이브러리다.
</p>
<svg>
<title>Delete</title>
<g><path/></g>
</svg>
const spanReact = getByTitle('React');
const svgDelete = getByTitle('Delete');
<input value="text" />;
const input = getByDisplayValue('text');
<span role="button">삭제</span>;
const spanRemove = getByRole('button');
<div data-testid="commondiv">흔한 div</div>;
const commonDiv = getByTestId('commondiv');
주의!
- camelCase 형식으로 작성X.
- 값을 설정할때 data-testid="..." 와 같이 작성
- ByTestId 는 다른 방법으로 선택할 수 없을때에만 사용.
1. getByLabelText
2. getByPlaceholderText
3. getByText
4. getByDisplayValue
5. getByAltText
6. getByTitle
7. getByRole
8. getByTestId
ex) fireEvent 로 버튼을 클릭하면 fireEvent.click(button) 버 튼이 focus X.
But, userEvent로 클릭하면 userEvent.click(button) 버튼이 focus O.
이렇게 실제 사용하는 유저가 보기에 실제 버튼을 클릭하는 행위가 더 잘 표현되기에 userEvent를 사용하는 게 더 추천되는 방법.
참고 문서 : https://testing- library.com/docs/queries/about/#priority