Jest와 React-Testing-Library를 이용해서 컴포넌트 렌더링 테스트를 진행한다.
다음과 같은 스탭으로 점진적으로 발전시키면서 진행한다.
import { useState } from 'react';
export default function ToggleButton() {
const [isOn] = useState(false);
return <button>{isOn ? 'ON' : 'OFF'}</button>;
}
import { render, screen } from '@testing-library/react';
import ToggleButton from './UserProfile';
// describe: 테스트를 그룹화한다.
describe('토글 버튼 컴포넌트', () => {
test('초기 상태가 false인 경우 OFF를 렌더링한다.', () => {
render(<ToggleButton />);
const buttonElement = screen.getByRole('button');
expect(buttonElement).toHaveTextContent('OFF');
});
});
import { useState } from 'react';
export default function ToggleButton() {
const [isOn, setIsOn] = useState(false);
const toggle = () => {
setIsOn((prev) => !prev);
};
return <button onClick={toggle}>{isOn ? 'ON' : 'OFF'}</button>;
}
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ToggleButton from './ToggleButton';
// describe: 테스트를 그룹화한다.
describe('토글 버튼 컴포넌트', () => {
test('초기 상태가 false인 경우 OFF이 렌더링된다.', () => {
render(<ToggleButton />);
const buttonElement = screen.getByRole('button');
expect(buttonElement).toHaveTextContent('OFF');
});
test('버튼을 클릭하면 ON이 렌더링된다.', () => {
render(<ToggleButton />);
const buttonElement = screen.getByRole('button');
// userEvent: 사용자의 행동을 시뮬레이션한다.
userEvent.click(buttonElement);
expect(buttonElement).toHaveTextContent('ON');
});
});
위와 같이 테스트 코드를 짜면 에러가 발생하는데
textContent가 ON이어야 하는데 테스트 결과 OFF를 받는다.
현재 상태가 useState로 관리되기 때문에 상태 변화가 비동기적으로 이루어 지기 때문이다.
따라서 이런 경우 waitFor, findBy, waitForElementToBeRemoved 같은 비동기 테스트를 위한 메서드를 사용해야한다.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ToggleButton from './ToggleButton';
// describe: 테스트를 그룹화한다.
describe('토글 버튼 컴포넌트', () => {
test('초기 상태가 false인 경우 OFF이 렌더링된다.', () => {
render(<ToggleButton />);
const buttonElement = screen.getByRole('button');
expect(buttonElement).toHaveTextContent('OFF');
});
test('버튼을 클릭하면 ON이 렌더링된다.', async () => {
render(<ToggleButton />);
// userEvent: 사용자의 행동을 시뮬레이션한다.
await userEvent.click(screen.getByRole('button'));
// 현재 컴포넌트의 경우 상태 변화가 useState를 통해 이루어져 비동기적으로 이루어진다.
// 따라서 이런 경우 비동기 테스트를 위한 메서드를 사용해야 한다.
// waitFor, findBy, waitForElementToBeRemoved 등의 메서드를 사용할 수 있다.
const buttonElement = await screen.findByRole('button');
expect(buttonElement).toHaveTextContent('ON');
});
});
import { useState } from 'react';
export default function ToggleButton() {
const [isOn, setIsOn] = useState(false);
const [name, setName] = useState('');
const toggle = () => {
setIsOn((prev) => !prev);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};
return (
<div>
<button onClick={toggle}>{isOn ? 'ON' : 'OFF'}</button>
<input type='text' aria-label='Name' value={name} onChange={handleChange} />
<p>{name}</p>
</div>
);
}
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ToggleButton from './ToggleButton';
// describe: 테스트를 그룹화한다.
describe('토글 버튼 컴포넌트', () => {
test('초기 상태가 false인 경우 OFF이 렌더링된다.', () => {
render(<ToggleButton />);
const buttonElement = screen.getByRole('button');
expect(buttonElement).toHaveTextContent('OFF');
});
test('버튼을 클릭하면 ON이 렌더링된다.', async () => {
render(<ToggleButton />);
await userEvent.click(screen.getByRole('button'));
const buttonElement = await screen.findByRole('button');
expect(buttonElement).toHaveTextContent('ON');
});
test('입력값이 변경되면 입력값이 렌더링된다.', async () => {
render(<ToggleButton />);
const inputElement = screen.getByLabelText('Name');
await userEvent.type(inputElement, 'Hello, world!');
const displayElement = await screen.findByText('Hello, world!');
expect(displayElement).toBeInTheDocument();
});
});
GPT를 이용한 예제 코드 실습