본문으로 건너뛰기
입문20

단위 테스트란 무엇인가: 범위, AAA 패턴, 통합 테스트와의 차이

단위 테스트는 코드에서 가장 작은 단위를 독립적으로 검증한다. 통합 테스트와의 범위 차이, 그리고 Arrange-Act-Assert라는 공통 구조를 이해한다.

  • #단위테스트
  • #통합테스트
  • #AAA패턴

개념

단위(Unit)란 무엇인가

"단위"는 보통 함수 하나, 또는 클래스의 메서드 하나처럼 더 이상 쪼개기 애매한 가장 작은 코드 조각을 뜻한다. 단위 테스트(Unit Test)는 이 작은 조각 하나를, 다른 코드나 외부 시스템과 분리한 채 독립적으로 검증한다.

function calculateDiscount(price, rate) {
  return price - (price * rate)
}
 
// 단위 테스트: 이 함수 하나만 떼어내서 검증
test("10% 할인율이면 가격이 10% 줄어든다", () => {
  expect(calculateDiscount(10000, 0.1)).toBe(9000)
})

통합 테스트(Integration Test)와의 범위 차이

단위 테스트가 함수 하나를 검증한다면, 통합 테스트는 여러 단위가 서로 맞물려 실제로 잘 동작하는지를 검증한다.

단위 테스트: calculateDiscount() 함수 하나만 검증
통합 테스트: 장바구니 API가 실제 데이터베이스와 연결돼서
             주문 생성→할인 계산→저장까지 전체 흐름이 맞는지 검증
단위 테스트통합 테스트
범위함수/클래스 하나여러 컴포넌트의 상호작용
속도매우 빠름(밀리초 단위)상대적으로 느림(DB·네트워크 관여)
외부 의존성모듈 9(레슨 9)에서 배운 것처럼 분리·대체함(레슨 3에서 다룬다)실제(또는 실제에 가까운) 의존성을 그대로 씀
실패 시 원인 파악매우 명확함(그 함수 자체의 문제)상대적으로 넓음(여러 컴포넌트 중 어디인지 확인 필요)

AAA 패턴 — 거의 모든 테스트 코드의 공통 구조

프레임워크가 무엇이든(레슨 2에서 다룬다), 테스트 코드는 대부분 이 세 단계로 구성된다.

test("VIP 회원은 배송비가 무료다", () => {
  // Arrange — 테스트에 필요한 상황을 준비한다
  const cart = { total: 10000, memberLevel: "VIP" }
 
  // Act — 실제로 검증하려는 동작을 실행한다
  const result = isEligibleForFreeShipping(cart.total, cart.memberLevel)
 
  // Assert — 결과가 기대한 대로인지 확인한다
  expect(result).toBe(true)
})
  • Arrange(준비) — 모듈 3에서 배운 사전조건과 같은 개념
  • Act(실행) — 실제 테스트 대상 동작
  • Assert(확인) — 모듈 3의 기대 결과와 같은 개념

이 구조를 알면, 문법이 낯선 언어로 짜인 테스트 코드를 봐도 "이 부분은 준비, 이 부분은 실행, 이 부분은 확인이구나"를 짚어낼 수 있다.

왜 QA가 이걸 읽을 줄 알아야 하는가

개발자가 "이 기능은 단위 테스트로 이미 커버돼 있어요"라고 말할 때, QA가 그 테스트 코드를 열어 실제로 어떤 케이스를 검증하고 있는지 읽을 수 있으면, "이 경계값은 테스트에 없네요"처럼 구체적인 피드백을 줄 수 있다. 이건 모듈 7(레슨 5)에서 배운 TDD/BDD 워크숍에서의 QA 역할과 정확히 이어진다.

실무에서 왜 필요한가

"단위 테스트를 다 통과했다"는 말이 "이 기능이 완벽하다"는 뜻은 아니다 — 단위 테스트는 각 조각이 개별적으로 맞다는 것만 보장하고, 조각들이 실제로 맞물렸을 때의 문제(통합 테스트의 영역)나 사용자 관점의 문제(수동 탐색적 테스트의 영역, 모듈 5)는 못 잡는다. QA가 단위 테스트의 범위와 한계를 정확히 이해하면, "이미 테스트됐으니 괜찮다"는 막연한 안도감 대신 "이 부분은 단위 테스트로 커버되지만, 저 부분은 별도로 확인해야 한다"는 구체적인 판단을 할 수 있다.

실습 과제

과제 1 — AAA 구조 짚어보기 (10분)

다음 테스트 코드에서 Arrange, Act, Assert에 해당하는 부분을 각각 표시한다.

test("재고가 0이면 구매 버튼이 비활성화된다", () => {
  const product = { name: "한정판 텀블러", stock: 0 }
  const isDisabled = isPurchaseButtonDisabled(product)
  expect(isDisabled).toBe(true)
})

과제 2 — 단위 vs 통합 판단하기 (10분)

다음 두 테스트가 각각 단위 테스트인지 통합 테스트인지 판단하고 이유를 적는다.

  1. "할인율 계산 함수에 0.2를 넣으면 20% 할인된 값을 반환한다"를 확인하는 테스트
  2. "회원가입 API를 호출하면 실제 데이터베이스에 사용자가 생성되고, 환영 이메일 발송 큐에 작업이 들어간다"를 확인하는 테스트

자가 체크리스트

  • 단위 테스트가 검증하는 범위를 설명할 수 있다
  • 단위 테스트와 통합 테스트의 속도·범위·실패 원인 파악 차이를 설명할 수 있다
  • AAA(Arrange-Act-Assert) 패턴을 테스트 코드에서 식별할 수 있다
  • "단위 테스트를 통과했다"는 말의 한계를 설명할 수 있다

흔한 실수

  • "단위 테스트를 다 통과했으니 이 기능은 완벽하다"고 오해한다. 단위 테스트는 조각 하나하나의 정확성만 보장할 뿐, 통합 시나리오나 사용자 경험까지 보장하지 않는다.
  • 단위 테스트와 통합 테스트를 속도나 이름만으로 구분하고 실제 범위를 확인하지 않는다. 파일 이름에 "test"가 들어있다고 전부 같은 성격의 테스트는 아니다.
  • 테스트 코드를 아예 읽지 않고 "테스트가 있다"는 말만 믿는다. AAA 구조만 알아도 어떤 케이스가 실제로 검증되는지 확인할 수 있다.

참고 자료