[xUnit 테스트 패턴] 11장 - 테스트 대역 사용
11장 - 테스트 대역 사용
간접 입력과 간접 출력
- 의존 컴포넌트의 리턴 값이나 예외를 실제로 발생시키기 어려울 때 테스트 대역으로 해결 할 수 있다.
간접 입력을 신경 써야 하는 이유
- DOC를 호출 후 리턴 값이나 예외를 처리하기 위한 목적으로 마련 됨
- 이런 경로들을 테스트하지 않고 둔다면
테스트 안 된 코드
가 된다.
- 이런 경로들을 테스트하지 않고 둔다면
- DOC에서 발생시키고자 하는 예외가
간접 입력 테스트 조건의 좋은 예
간접 출력을 신경 써야 하는 이유
- 뒷문 테스트 해야 하는 경우
- 메시지 로깅
- 간접 출력 테스트 조건
- 메시지가 로그로 남게 되는 환경
- 일부 간접 출력은 메소드 호출이나 메시지 형태로 다른 컴포넌트에 전달 된다.
간접 입력은 어떻게 제어할 수 있을까
- 자신이 원하는 모든 유형의 리턴 값을 DOC가 리턴하게 제어할 수 있어야 한다.
- 제어 위치를 통해 발생시키고 싶은 간접 입력의 유형
- 메소드/함수의 러턴 값
- 변경 가능한 인자 값(pass-by-reference로 전달한 함수 인자)
- 예상되는 예외
테스트스텁
으로 함수가 원하는 값을 리턴하게 설정한 후 이를 SUT에 설치- SUT가 실행되는 동안 테스트 스텁은 함수 호출을 받아 이전에 설정된 값을 리턴한다.
간접 출력은 어떻게 검증할 것인가
- 간접 출력을 테스트하려면 SUT가 호출하는 DOC의 API를 관찰할 수 있어야 한다.
- 리턴 값도 제어할 수 있어야 한다.
- 간접 출력 검증 두 가지 방식
- 절차형 동작 검증
- SUT가 실행되는 동안 특정 DOC로의 호출을 잡아내 SUT 실행이 끝난 후 원하는 대로 호출됐는지를 검사
- 기대 동작 검증
- 테스트의 픽스처 설치 단계에서
동작 명세
를 만든 후 이를 실제 동작과 비교
- 테스트의 픽스처 설치 단계에서
- 절차형 동작 검증
대역으로 테스트하기
테스트 대역
은 협조적이고 원하는 대로 테스트를 작성할 수 있게 해주는 객체의 일종이다.
테스트 대역의 종류
- 테스트 대역(Test Double)
- 테스트의 실행 목적을 나타내기 위해 실제 컴포넌트 대신 설치하는 객체나 컴포넌트
- 테스트 대역의 동작 방식 4가지
- 더미 객체(Dummy Object)
- SUT 인자로 넘겨주긴 하지만 실제로는 쓰이지 않는 객체
- 자리 채움용
- 테스트 스텁(Test Stub)
- SUT가 의존하는 실제 컴포넌트를 대신해 테스트가 SUT의 간접 입력을 제어할 수 있게 해주는 객체
- 테스트 스파이(Test Spy)
- 테스트 스텁의 업그레이드 버전, SUT의 실행이 끝난 후 테스트가 자신을 검사해 SUT의 간접 출력을 검증할 수 있게 한다.
- 모의 객체(Mock Object)
- SUT가 의존하는 실제 컴포넌트를 대체해 테스트가 SUT의 간접 출력을 검증할 수 있게 한다.
- 가짜 객체(Fake Object)
- 실제 DOC의 기능을 다르게 구현해 대체한 객체
- 더미 객체(Dummy Object)
더미 객체
- 테스트 대역이 퇴화된 형태
- 메소드에서 메소드로 주고받는 것 외에는 전혀 쓸모가 없다.
- 널 객체 와는 다르다
- SUT에서 쓰지 않으므로 더미 객체가 어떻게 동작하는지 걱정할 필요 없다.
테스트 스텁
- 자신의 메소드가 호출될 때 SUT로 간접 입력 값을 보내는 제어 위치로 사용되는 객체
- 응답기(Responder)
- 메소드가 호출될 때 정상/비정상 간접 입력을 SUT로 보내주는 기본적인 테스트 스텁
- 훼방꾼(Saboteur)
- 예외나 에러를 발생시켜 SUT에 비정상 간접 입력을 보내주는 특수 테스트 스텁
테스트 스파이
- SUT의 간접 출력에 대한 관찰 위치로 쓰이는 객체
- 테스트 스텁의 기능에 더해 테스트 스파이에서는 SUT가 자신의 메소드를 호출한 내역을 기록할 수 있다.
- 검증 단계에서는 테스트 스파이로부터 실제로 호출된 내역과 기대 호출 내역을 비교하는 절차형 동작 검증을 한다.
모의객체
- SUT의 간접 출력에 대한 관찰 위치로 쓰이는 객체
- 테스트 스텁과 마찬가지로 메소드가 호출되면 정보를 리턴해야 함
- SUT가 자신을 어떻게 호출했는지 신경 씀
- 테스트 스파이와 다른점
- 미리 정의한 기대 값과 실제 호출을 단언문으로 비교해 문제가 있으면 테스트 메소드를 대신해 테스트를 실패 시킨다.
- 모든 테스트에서 같은 모의 객체로 SUT의 간접 출력을 검증하는 로직을 재사용할 수 있다.
- 엄격한 모의 객체
- 정상 호출이여도 명시된 것과 순서가 다르면 테스트 실패
- 관대한 모의 객체
- 호출 순서가 달라도 넘어감.
- 실제로 호출된 메소드에 대해서만 그에 해당하는 기대 호출을 검증
가짜 객체
- 테스트에 의해 제어되지 않고 관찰되지도 않는다.
- 실제 DOC의 기능 중 전체나 일부를 단순하게 구현
- 가짜 객체 쓰는 이유
- 너무 느리거나, 테스트 환경에서 쓸 수 없을 때
테스트 대역 제공하기
- 테스트 제공하는 방법 두가지
- 직접 만든 테스트 대역
- 하드 코딩 한 것
- 의사 객체(기본 상위 클래스 집합)
- 테스트 스텁, 테스트 스파이, 모의 객체에서 호출 될 것으로 예상 되는 여러 메소드를 한데 모은다.
- 동적 생성 테스트 대역
- 다른 개발자가 제공하는 프레임워크나 툴킷으로 실행 시간에 생성하는 것
- 직접 만든 테스트 대역
테스트 대역 설정하기
- 테스트 스텁, 모의 객체
- 기대 값이 무엇인지, 어떤 값을 리턴해야 하는지 알려줘야 한다.
- 테스트 스텁, 테스트 스파이
- SUT가 호출할 메소드가 리턴하는 값을 설정
- 모의 객체
- SUT가 호출할 것으로 예상하는 모든 메소드의 이름과 인자를 설정
- 가짜 객체, 더미 객체
- 설정할 필요 없음
테스트 대역 설치하기
- SUT에게 테스트 대역을 쓰게 알려주는 것
- 테스트 대역 설치 순서
- 테스트 대역 생성
- 설정되는 테스트 대역이라면 필요한 설정하기
- SUT 실행하기 전이나 실행할 때 테스트 대역을 대신 사용하게 만들기
- 테스트 대역 설치 방법
- 의존 주입 : 클라이언트 소프트웨어가 SUT에게 어떤 DOC를 쓸지 알려준다.
- Setter method로 주입
- 생성자에서 주입
- 인자로 주입
- 의존 찾기 : SUT가 DOC의 생성/얻기를 다른 객체에 위임
- 객체 팩토리
- SUT에서 DOC를 직접 생성하지 않고 대신 잘 알고 있는 객체에 있는 팩토리 매소드를 호출해 DOC 생성
- 서비스 탐지기
- SUT는 잘 알고 있는 레지스트리 객체를 통해 이전에 만들어진 서비스 객체를 얻는다.
- 객체 팩토리
- 테스트 훅 : SUT 내에서 DOC나 DOC로의 호출을 변경
- 레거시 코드에 테스트를 도입할 때 위에 기법들이 도입하기 어려울 때 사용
- 의존 주입 : 클라이언트 소프트웨어가 SUT에게 어떤 DOC를 쓸지 알려준다.
테스트 대역의 다른 용도
내시경 테스팅
- 모의 객체를 테스트 대상 메소드의 인자로 넘겨 SUT를 안에서 테스트 한다.
- 밖에서는 보이지 않는 SUT의 특정 내부 동작도 검증 가능
필요 주도 개발
- 내시경 테스팅이 발전한 형태
- 테스트를 작성하면서 동시에 SUT의 의존을 정의
- ‘밖에서 안으로’ 작성하고 테스트하는 방법
픽스처 설치 빠르게 하기
- 테스트 대역은 신선한 픽스처의 설치 시간을 줄일 때도 쓰인다.
- 엔티티 체인 스니핑
- 여러 객체의 집합 대신 단일 테스트 대역을 생성
테스트 실행 빠르게 하기
- 가짜 데이터베이스 구현
기타 고려 사항
- 실제 DOC가 제대로 설치됐는지 검증
- 생성자 테스트
- 새로 산 망치의 오류
- 남용하게 되는 심하게 명세된 소프트웨어가 된다.