Chapter 2

단위 테스트 정의와 구조

  • 2.1 단위 테스트의 정의
  • 2.2 고전파와 런던파
  • 2.3 의존성의 종류
  • 2.4 AAA 패턴
  • 2.5 테스트 이름 짓기
  • 2.6 매개변수화 테스트

"단위가 뭔가"라는 질문에서 시작해야 해. 이걸 어떻게 보느냐에 따라 테스트 전체 설계 방향이 달라지거든. 그리고 단위가 뭔지 알았으면, 그 단위를 실제로 어떻게 테스트로 작성하는지도 바로 이어서 봐야 해.

단위 테스트는 작은 코드 조각을 검증하고, 빠르게 실행되고, 격리된 방식으로 실행돼. 처음 두 개는 이견이 없어. 문제는 "격리"야. 격리를 어떻게 보느냐에서 고전파와 런던파가 갈려.

런던파는 격리를 "단위 간 격리"로 봐. 테스트 대상 외의 모든 의존성을 목으로 대체해서 클래스 하나만 완전히 고립시켜야 한다는 입장이야. 고전파는 격리를 "테스트 간 격리"로 봐. 테스트들이 서로 영향을 주지 않으면 되고, 여러 클래스를 함께 테스트해도 상관없어. 공유 의존성만 목으로 대체하면 돼. 저자는 고전파를 지지해. 런던파처럼 모든 걸 모킹하면 테스트가 구현 세부사항에 결합되거든. 내부 구조가 바뀌면 기능은 그대로인데 테스트가 깨지는 상황이 생겨. LLM한테 "테스트 짜줘"하면 의존성 다섯 개 달린 목 범벅 테스트가 나오는 게 바로 이 런던파 함정이야.

그럼 뭘 목으로 대체해야 하냐면, 공유 의존성과 가변 의존성이야. 공유 의존성은 테스트 간에 공유되는 것 — 우리 DB, static 변수 등. 가변 의존성은 비결정적이거나 사이드 이펙트가 있는 것 — 현재 시간, 이메일 서비스. 반면 각 테스트에 독립적으로 새로 생성되는 객체들은 실제로 써도 돼. 클래스 경계라서가 아니라, 공유되거나 가변적이어서 목을 쓰는 거야.

단위가 뭔지 정리됐으면, 실제로 어떻게 쓰는지로 넘어가자. 좋은 테스트는 Arrange - Act - Assert 세 단계로 구성돼. 준비하고, 실행하고, 검증하지. 각 단계는 빈 줄로 구분해. Act가 두 줄 이상이면 SUT 공개 API에 문제가 있다는 신호야. 캡슐화를 점검해야 해. 주석 없이 읽기 어려운 테스트라면 복잡한 게 문제지, 주석이 해결책이 아니야.

테스트 이름은 IsDeliveryValid_InvalidDate_ReturnsFalse 같은 기술적인 방식보다 평문 문장으로 써야 해. Delivery_with_invalid_date_should_be_considered_invalid처럼. 도메인 전문가가 읽을 수 있어야 하고, SUT 메서드 이름을 그대로 쓰면 안 돼. 구현이 바뀌면 이름도 바꿔야 하고, 무엇보다 "어떤 동작을 검증하는가"를 설명하지 못하거든.

비슷한 케이스가 여러 개면 매개변수화 테스트로 묶을 수 있어. 단, 긍정 케이스와 부정 케이스를 억지로 하나로 합치지는 마. 단순한 케이스만 묶고, 성격이 다른 케이스는 별도 테스트로 두는 게 나아.


정리

2장 읽고 기억할 거 네 가지:

  1. 고전파 vs 런던파는 "격리"의 정의 차이야. 런던파는 클래스 단위 격리, 고전파는 테스트 간 격리. 저자는 고전파 지지
  2. 목은 공유/가변 의존성에만: 클래스 경계라서가 아니야. 모든 걸 목으로 대체하면 리팩토링마다 테스트가 깨져
  3. AAA 패턴으로 구성해: Act가 두 줄이면 SUT 설계를 의심해
  4. 테스트 이름은 평문으로: 도메인 언어로, SUT 메서드 이름 그대로 쓰지 마