개념
화면 조각도 "단위"가 될 수 있다
레슨 1에서 단위 테스트를 "함수 하나를 독립적으로 검증"하는 것으로 배웠다. 프런트엔드에서는 이 "단위"가 버튼, 입력 폼, 상품 카드 같은 화면 컴포넌트 하나가 된다. 컴포넌트 테스트는 이 화면 조각을 실제 브라우저 전체를 띄우지 않고도 독립적으로 검증한다.
구현이 아니라 사용자가 보는 것을 테스트한다
컴포넌트 테스트에서 가장 중요한 원칙은, "내부 코드가 어떻게 짜였는지"가 아니라 "사용자가 화면에서 무엇을 보고 어떻게 조작하는지"를 기준으로 테스트한다는 것이다.
// 나쁜 예 — 내부 구현(CSS 클래스 이름)에 의존
expect(wrapper.find(".btn-primary-disabled")).toHaveLength(1)
// 좋은 예 — 사용자가 실제로 인식하는 것(버튼 텍스트, 비활성화 상태)에 의존
const button = screen.getByRole("button", { name: "구매하기" })
expect(button).toBeDisabled()CSS 클래스 이름은 디자인이 바뀌면 언제든 바뀔 수 있는 구현 세부사항이다. 반면 "구매하기라는 이름의 버튼이 비활성화되어 있다"는 것은 실제 사용자가 화면에서 확인하는 사실 그대로다. 이 원칙을 지키면, 내부 코드를 리팩터링해도(겉모습이 그대로면) 테스트가 깨지지 않는다.
사용자 행동을 흉내 내기
render(<CouponForm />)
const input = screen.getByLabelText("쿠폰 코드")
fireEvent.change(input, { target: { value: "SAVE10" } })
const applyButton = screen.getByRole("button", { name: "적용" })
fireEvent.click(applyButton)
expect(screen.getByText("10% 할인이 적용되었습니다")).toBeInTheDocument()이 코드는 실제 사용자가 하는 행동(쿠폰 코드 입력 → 적용 버튼 클릭)을 그대로 흉내 내고, 그 결과 화면에 무엇이 보이는지(할인 적용 안내 문구)를 확인한다. 모듈 3에서 배운 테스트 케이스의 절차·기대결과 구조와 본질적으로 같다.
어디까지가 컴포넌트 테스트의 영역인가
- 컴포넌트 테스트가 확인하는 것: 이 화면 조각이 주어진 상태에서 올바르게 표시되는가, 사용자 조작에 올바르게 반응하는가
- 컴포넌트 테스트가 확인하지 않는 것: 실제 서버 API 호출이 제대로 되는가(통합 테스트나 E2E 테스트의 영역), 여러 화면에 걸친 전체 흐름(모듈 5의 탐색적 테스팅이나 E2E의 영역)
컴포넌트 테스트는 API 호출 부분을 레슨 3에서 배운 테스트 더블로 대체해서, "화면 로직만" 독립적으로 검증하는 경우가 많다.
실무에서 왜 필요한가
프런트엔드 개발자가 "이 컴포넌트는 테스트로 커버돼 있어요"라고 할 때, 그 테스트가 실제 화면 요소(버튼 이름, 표시되는 텍스트) 기준인지, 아니면 내부 구현(CSS 클래스, 컴포넌트 내부 상태 변수) 기준인지 QA가 구분할 수 있으면, "이 테스트는 디자인이 바뀌면 오히려 깨지기 쉬운 구조네요"처럼 유용한 피드백을 줄 수 있다. 이건 스테이지 4(UI 자동화)에서 다룰 E2E 테스트 설계 원칙과도 그대로 이어진다.