Chapter 8

단위 테스트 안티 패턴

  • 11.1 비공개 메서드 테스트
  • 11.2 비공개 상태 노출
  • 11.3 테스트로 도메인 지식 누출
  • 11.4 구체 클래스 목 처리
  • 11.5 시간에 의존하는 테스트

"이 private 메서드, 테스트해야 하지 않나?" 이런 생각이 든 적 있으면 11장을 잘 봐야 해. 흔히 저지르는 안티패턴들을 정리하는 마지막 챕터야.

private 메서드는 테스트하지 마. private 메서드는 구현 세부사항이야. 이걸 테스트하면 테스트가 구현에 결합돼. 메서드 이름만 바꿔도 테스트가 깨지거든. private 메서드의 로직이 테스트할 만큼 중요하다면, 그건 그 로직을 별도 public 클래스로 추출해야 한다는 신호야. 그 클래스를 테스트하면 돼.

비슷한 맥락에서 테스트를 위해 private 필드나 프로퍼티를 public으로 바꾸는 것도 안티패턴이야. 프로덕션 코드를 테스트에 맞춰 변형하면 안 돼. 내부 상태를 확인해야 한다면 외부로 드러나는 결과를 통해 간접적으로 검증해야 해. 설계가 잘못됐다는 신호거든.

테스트 코드에 비즈니스 규칙을 다시 구현하는 것도 문제야. 프로덕션 로직을 테스트에서 똑같이 계산해서 기댓값으로 쓰면, 프로덕션 코드에 버그가 있어도 테스트가 같은 버그를 가지고 있어서 통과해버려. 테스트에는 하드코딩된 기댓값을 써야 해. 값이 어디서 나왔는지 명확하게.

인터페이스 대신 구체 클래스를 목으로 쓰는 것도 피해야 해. 구체 클래스를 목으로 대체하려면 상속을 써야 하는데, 이러면 부모 클래스 구현에 의존하게 돼. 인터페이스를 통해 의존성을 주입하면 목 처리도 깔끔해지고 설계도 더 유연해져.

마지막으로 DateTime.NowDate.now()를 코드 안에서 직접 쓰지 마. 테스트가 비결정적이 돼. 오늘 실행하면 통과하고 내일 실행하면 실패할 수 있거든. IDateTimeProvider 같은 인터페이스를 만들어서 주입해. 테스트에서는 고정된 시간을 반환하는 구현체를 쓰면 돼. 시간뿐 아니라 랜덤값도 마찬가지야.


정리

11장 읽고 기억할 거 세 가지:

  1. private 메서드를 테스트하지 마: 중요한 로직이면 별도 클래스로 추출해서 public으로 테스트해
  2. 테스트를 위해 프로덕션 코드를 변형하지 마: 내부 상태를 노출하거나 접근 제어자를 바꾸는 건 설계가 잘못됐다는 신호야
  3. 시간, 랜덤 같은 비결정적 요소는 주입해: 테스트가 항상 동일한 결과를 내려면 외부 상태를 코드 안에 직접 쓰면 안 돼