프론트엔드 테스트는 네 가지로 구분되며 각기 다른 중요도를 갖습니다.
위 그림의 트로피가 보여주는 이러한 테스트 형식의 크기는 애플리케이션을 테스트할 때 얼마나 집중해야 하는지를 나타냅니다. 일반적으로 코드 작성 단계에서는 정적 분석을 폭넓게 활용하고, 핵심 로직에는 단위 테스트를 작성하며, 주요 기능 흐름은 통합 테스트로 검증하고, 최종 사용자 시나리오는 E2E 테스트로 최소한의 핵심 경로를 점검하는 전략을 취하는게 일반적입니다.
정적 테스트는 소스코드를 실행하지 않고 정적으로 분석하여 오류를 찾는 테스트 기법입니다. 즉 개발자가 코드를 작성하는 과정에서 오타 혹은 타입 에러를 잡아내어 실행 전에 버그를 예방합니다. 대표적인 도구로 ESLint
와 Typescript 컴파일러
가 있습니다.
type User = { name: string; age: number };
const Greeting = ({ user }: User) => {
// 오류: user.age는 number 타입인데 함수처럼 호출되고 있습니다.
return <div>Hello, {user.name}. You are {user.age()} years old.</div>;
};
유닛 테스트는 가장 작은 단위의 테스트로, 함수 혹은 컴포넌트를 외부 의존성 없이 검증하는 테스트입니다.
각 기능을 개별 실행하여 예상한 결과가 나오는지 확인하는 테스트로, 대표적인 도구로 Jest
, Vitest
등이 있습니다.
// Hello 컴포넌트 (유닛 테스트 대상)
type HelloProps = { name: string };
const Hello = ({ name }: HelloProps) => <h1>Hello, {name}!</h1>;
// Hello 컴포넌트에 대한 유닛 테스트 (Jest + Testing Library)
import { render, screen } from "@testing-library/react";
import Hello from "./Hello";
test("Hello 컴포넌트는 전달된 이름을 화면에 표시한다", () => {
render(<Hello name="Alice" />);
expect(screen.getByText("Hello, Alice!")).toBeInTheDocument();
});
통합 테스트는 애플리케이션의 여러 구성 요소나 모듈이 조화롭게 상호작용하며 동작하는지 확인하는 테스트입니다. 개별 기능이 아닌 전체 기능의 동작에 초점을 두기 때문에, 사용자가 애플리케이션을 문제없이 사용할 수 있는지를 확인하는 데 중요합니다. 이러한 통합 테스트를 작성할 때는 주로 React Testing Library
를 활용하며, 이를 통해 실제 사용자 관점에서 컴포넌트들을 렌더링하고 이벤트를 일으켜 봄으로써 구현 세부사항이 아닌 기능적 결과를 검증합니다
// 테마 컨텍스트와 이를 소비하는 컴포넌트
const ThemeContext = React.createContext("light");
const ShowTheme = () => {
const theme = React.useContext(ThemeContext);
return <div>Theme: {theme}</div>;
};
// 통합 테스트 (컨텍스트 제공자 + 소비자 통합 동작 검증)
import { render, screen } from "@testing-library/react";
test("ShowTheme 컴포넌트가 컨텍스트의 테마 값을 표시한다", () => {
render(
<ThemeContext.Provider value="dark">
<ShowTheme />
</ThemeContext.Provider>
);
expect(screen.getByText("Theme: dark")).toBeInTheDocument();
});
E2E(End-to-End) 테스트는 실제 브라우저 환경에서 애플리케이션을 구동하여 사용자 시나리오 전반을 처음부터 끝까지 검증하는 테스트입니다. 브라우저를 통해 사용자 동작을 단계별로 자동화한 시나리오를 실행합니다. 대표적인 도구로는 Cypress
와 Playwright
가 있습니다.
test('Todo 앱 - 새 할 일 추가', async ({ page }) => {
await page.goto('/todo');
await page.getByRole('textbox', { name: "제목을 입력해주세요." }).fill('Write tests');
await page.keyboard.press('Enter');
await expect(page.locator('ul.todo-list')).toContainText('Write tests');
});
참고
[10분 테코톡] 헤다의 프론트엔드 테스트 종류
[인턴일지] 프론트엔드 개발에서 테스트가 필요한가요?
프론트엔드에서 테스트코드를 작성하는 방법
우리 프론트엔드 팀에는 어떤 테스트를 적용해야 할까?
Frontend Testing Strategy
프론트엔드에서의 Static, Unit, Integration, E2E 테스트
Learn the smart, efficient way to test any JavaScript application.