박종훈 기술블로그

테스트 대역 (목 과 스텁) - 5장 목과 테스트 취약성 (1)

단위테스트 (블라디미르 코리코프)
5장 목과 테스트 취약성


0. 개요

  • 런던파 : 테스트 대상 코드 조각을 서로 분리하고 불변 의존성을 제외한 모든 의존성에 테스트 대역을 써서 격리하자.
  • 고전파 : 단위 테스트를 분리해서 병렬로 실행할 수 있게 하자. 테스트 간에 공유하는 의존성에 대해서만 테스트 대역을 사용하자.

테스트 대역
모든 유형의 비운영 가짜 의존성을 설명하는 포괄적인 단어

  • 영화의 스턴트 대역 이라는 개념에서 비롯되었음.

double에 대역 이라는 뜻이 있음.
double in dictionary

목(mock)
테스트 대상 시스템(SUT)과 그 협력자 사이의 상호 작용을 검사할 수 있는 테스트 대역

목은 리팩터링 내성이 부족한 테스트를 초래할 수도 있지만 목을 사용하는게 바람직한 경우도 있다.

목과 테스트 취약성 사이에는 깊고 불가피한 관련이 있다.
어떻게 관련 있는지. 리팩터링 내성 저하 없이 목을 사용하려면 어떻게 해야하는지 에 대해 살펴본다.

1. 목과 스텁 구분

1.1 테스트 대역 유형

테스트 대역(test double)은 테스트 대상 시스템으로 실제 의존성 대신 전달되므로 설정이나 유지 보수가 어려울 수 있다.

xUnit 테스트 패턴 에 따르면, 테스트 대역에는 더미, 스텁, 스파이, 목, 페이크 라는 다섯 가지가 있다. 다섯 가지 변형은 미미한 구현 세부 사항 차이이다.

테스트 대역 유형은 목과 스텁의 두 가지 유형으로 나눌 수 있다.

test double categorize

외부로 나가는 상호 작용을 모방하고 검사하는 데 도움이 된다. 이러한 상호 작용은 SUT가 상태를 변경하기 위한 의존성을 호출하는 것에 해당한다.

스텁

내부로 들어오는 상호 작용을 모방하는데 도움이 된다. 이러한 상호 작용은 SUT가 입력 데이터를 얻기 위한 의존성을 호출하는 것에 해당한다.

mock vs stub

이메일 발송은 SMTP 서버에 사이드 이펙트를 초래하는 상호작용 즉 외부로 나가는 상호작용이다. 목은 이러한 상호 작용을 모방하는 테스트 대역에 해상한다.

데이터베이스에서 데이터를 검색하는 것은 내부로 들어오는 상호 작용이다. 사이드 이펙트를 일으키지 않는다. 해당 테스트 대역은 스텁이다.

목은 스텁이 아니다. - 마틴 파울러 원본 : https://martinfowler.com/articles/mocksArentStubs.html
번역 : https://jaime-note.tistory.com/330

목은 테스트 대상 시스템(SUT)와 관련 의존성 간의 상호 작용을 모방하고 검사하는 반면 스텁은 모방만 한다.
목은 동작 검증을, 스텁은 상태 검증을 한다.

다섯 가지 테스트 대역 차이 요약

  • 목(mock)

    • 스파이 : 수동으로 작성 (목과 동일한 역할을 함)
    • 목 : 목 프레임워크의 도움을 받음
  • 스텁(stub)

    • 더미 : 단순하고 하드코딩된 값
    • 스텁 : 시나리오마다 다른 값을 반환하게끔 구성할 수 있도록 필요한 것을 다 갖춘 완전한 의존성
    • 페이크 : 대다수의 목적에 부합하는 스텁과 같으나 보통 아직 존재하지 않는 의존성을 대체하고자 구현한다.

1.2 도구로서의 목과 테스트 대역으로서의 목

Mock은 두가지 의미가 있다.
목 라이브러리(mocking library)의 클래스도 목으로 부르며
테스트 대역으로서의 목 도 있다.

1.3 스텁으로 상호 작용을 검증하지 말라

테스트에서 거짓 양성을 피하고 리팩터링 내성을 향상시키는 방법은 구현 세부 사항이 아니라 최종 결과(이상적으로 비개발자들에게 의미가 있어야 함)를 검증하는 것 뿐이다.

최종결과가 아닌 사항을 검증하는 것을 과잉 명세(overspecification)이라고 부른다.

1.4 목과 스텁 함께 쓰기

목 과 스텁은 같이 사용할 수 있다.

1.5 목과 스텁은 명령과 조회에 어떻게 관련돼 있는가?

명령 조회 분리 (CQS, Command Query Seperation) 에 따르면
모든 메서드는 명령이거나 조회여야 하며, 이 둘을 혼용해서는 안된다.

명령은 사이드 이펙트를 일으키고 어떤 값도 반환하지 않는 메서드(void 반환)다.
조회는 그 반대로 사이드 이펙트가 없고 값을 반환한다.
* 사이드 이펙트의 예로는 객체 상태 변경, 파일 시스템 내 파일 변경 등이 있다.

mock vs stub 2

메서드가 사이드 이펙트를 일으키면 해당 메서드의 반환 타입이 void인지 확인하라.
메서드가 값을 반환하면 사이드 이펙트가 없어야 한다.
질문 할 때 답이 달라져서는 안된다.

항상 CQS 원칙을 따를 수 있는건 아니다. 그래도 가능하면 CQS 원칙을 따르는 것이 좋다.

명령을 대체하는 테스트 대역은 목이다. 조회를 대체하는 테스트 대역은 스텁이다.