Junuuu 2023. 6. 30. 00:01

5장에서 다루는 내용

  • Mock과 Stub 구분
  • 식별할 수 있는 동작과 구현 세부 사항 정의
  • 목과 테스트 취약성 간의 관계 이해
  • 리팩터링 내성 저하 없이 목 사용하기

 

테스트에서 Mock을 사용하는 것은 논란의 여지가 있는 주제입니다.

어떤 사람들은 훌륭한 도구라고 생각하며, 다른 사람들은 취약성을 초래한다고 말합니다.

 

테스트 대역

테스트 대역은 스턴트 대역이라는 개념에서 비롯되었습니다.

제라드 메스자로스에 따르면 테스트 대역은 더미, 스텁, 스파이, 목, 페이크 다섯 가지가 있습니다.

실제로는 목과 스텁의 두 가지 유형으로 나눌 수 있습니다.

 

Mock은 외부로 나가는 상호 작용을 모방하고 검사하는데 도움이 됩니다.

Stub은 내부로 들어오는 상호 작용을 모방하는데 도움이 됩니다.

 

이때 Stub과 상호작용하는 테스트를 하게 된다면 깨지기 쉬운 테스트가 될 수 있습니다.

예를 들어 stub.verify(x -> x.GetNumberOfUsers(), Times.Once)가 대표적인 예시입니다.

최종 결과가 아닌 사항을 검증하는 이런 관행을 과잉명세라 합니다.

 

CQS 관점에서 Mock과 Stub

보통 Command Query Separation 원칙과 관련이 있습니다.

Command는 명령은 update, delete, insert와 같이 부작용을 일으킵니다. (객체 상태 변경, 파일 시스템 내 파일 변경 등)

Query는 조회로 부작용이 없고 값을 반환합니다.

 

Command는 Mock, Query는 Stub과 일치합니다.

 

식별할 수 있는 동작과 구현 세부 사항

테스트는 어떻게가 아니라 무엇에 중점을 둬야 합니다.

책에서는 공개 API와 비공개 API를 예시로 설명하고 있습니다.

간단하게 이해했을때 객체의 캡슐화에 대해 설명하며 클래스가 이런 구현 세부 사항을 유출하는지 확인하면 됩니다.

 

계속해서 커지고 복잡도를 대처할 수 있는 방법은 실질적으로 캡슐화 말고는 없습니다.

코드 API가 해당 코드로 할 수 잇는것과 할 수 없는 것을 알려주지 않으면 코드가 변경되었을 때 모순이 생기지 않도록 많은 정보를 염두에 둬야 합니다.

 

이를 막기 위해서는 캡슐화를 올바르게 유지하며 개발자의 실수를 줄이고, 궁극적으로는 단위 테스트와 동일한 목표를 달성합니다.

 

만약 세부사항을 비공개로 하면 테스트가 식별할 수 있는 동작을 검증하는 것 외에는 다른 선택지가 없으며, 이로 인해 리팩터링 내성도 자동으로 좋아집니다.

 

이 정도에서 다음과 같은 생각이 듭니다.

"private 메서드를 테스트 해야 하는가?"

누군가는 reflection을 하라고 하며, 누군가는 테스트를 하지 말라고 합니다.

이 책의 관점으로 바라보았을 때는 이는 세부사항으로 검증하지 말아야 할 것 같습니다.

 

 

시스템 내부 통신과 시스템 간 통신

시스템 내부 통신은 구현 세부 사항이며, 시스템 간 통신은 그렇지 않습니다.

연산을 수행하기 위한 도메인 클래스 간의 협력은 식별할 수 있는 동작이 아니므로 시스템 내부 통신은 구현 세부 사항에 해당합니다.

반면 시스템 외부 환경과 통신하는 방식은 전체적으로 해당 시스템의 식별할 수 있는 동작을 나타냅니다.

 

목은 보통 시스템간 통신 패턴을 확인할 때 좋습니다. (메일 전송)

반대로 내부 통신에 사용하면 테스트가 구현 세부 사항과 결합되어 리팩터링 내성 지표가 미흡해집니다.

 

 

고전파를 선호하는 이유

런던 파는 불변 의존성을 제외한 모든 의존성에 목 사용을 권장합니다.

그 결과 테스트는 애플리케이션과 외부 시스템 간의 통신을 확인하는 것처럼 클래스 간 통신도 확인합니다.

이렇게 되면 구현 세부 사항에 결합되어 테스트에 리팩터링 내성이 사라집니다.

 

고전파는 SMTP 서비스나 메시지 버스 등 프로세스 외부 의존성에 대해서만 교체하므로 이 문제에 훨씬 유리합니다.

런던 파만큼은 아니지만 고전파로 목 사용을 장려합니다.

 

결론

테스트 간 통신의 검증하는 세부사항은 목을 사용하지 말고, 애플리케이션의 경계를 넘나드는 경우에만 목을 사용하자.