3장. 단위 테스트 구조
AAA패턴
테스트를 준비, 실행, 검증 세 부분으로 나눠서 작성하는 것
준비 구절: 테스트 대상 시스템(SUT)이나 해당 의존성을 원하는 상태로 만듦
실행 구절: SUT에서 메서드를 호출하면서 준비된 의존성 전달 / 출력값 캡처
검증 구절: 결과 검증. 이 때 결과는 반환값 or SUT과 협력자의 최종 상태 or SUT가 협력자에 호출한 메서드 등
Given-When-Then 패턴
AAA패턴과 같은 구성
차이점은 프로그래머가 아닌 사람에게 Given-When-Then 구조가 더 읽기 쉬움. 따라서 비기술자들과 공유하는 테스트의 경우 더 적합
지켜야할 것
여러개의 준비, 실행, 검증 구절 피하기. 각각은 한번만
여러개가 있다는 건 이미 너무 많은 것을 한 번에 검증한다는 것.
하지만 이미 ‘느린 통합 테스트’일 때 여러 실행과 검증이 있는 단일한 테스트로 묶을 수도 있음
테스트 내 if문 피하기: if 문 자체가 테스트가 한 번에 너무 많은 것을 검증한다는 증거
각 구절의 크기
일반적으로 준비 구절이 가장 큼
실행과 검증을 합친 만큼 커질 수 있음
하지만 이것보다 훨씬 크면 같은 테스트 클래스 내 비공개 메서드 또는 별도의 팩토리 클래스로 도출하는 것이 좋다
제품 코드에서도 생성자 복잡할 때 빌더 패턴 이용하는 것처럼...
이 때 두가지 패턴으로 오브젝트 마더/테스트 데이터 빌더 → 10.4.1절
실행 구절은 코드 한 줄
두 줄 이상인 경우? SUT의 공개 API에 문제가 있을 수 있음
예를 들어 어떤 동작이 끝나려면 무조건 여러 메소드를 호출해야한다는 걸 클라이언트가 알고 있어야 한다는 것과 같음
클라이언트에게 메서드 호출 강요하지말 것!
다음 메서드를 호출하지 않으면 문제 발생 → 불변 위반(invariant violation) | 코드 캡슐화를 지킬 것
다만 비즈니스 로직이 아닌 유틸리티나 인프라 코드는 덜 적용됨
검증 구절은 하나의 검증??
흔히 많이 듣는 검증 구절에 하나의 assert는 올바르지 않다! 단위 테스트의 단위는 동작의 단위이지 코드의 단위가 아니다.
단일 동작 단위는 여러 결과를 낼 수 있으며, 하나의 테스트로 모든 결과를 평가하는 것이 좋음
하지만 그래도 너무 커지는 건 경계할 것. 코드에 추상화가 누락된것일 수 있음
예) SUT에서 반환된 객체 내의 모든 속성 검증 대신 객체 클래스에 동등 멤버(equality member)를 정의하는 것이 좋음.
테스트 대상 시스템 구별하기
동작이 여러 클래스에 걸쳐 있을 수도 있지만 진입점은 오직 하나
따라서 SUT(진입점)를 의존성과 구분 → 아예 이름을 sut로 지어라 ❓
각 구절마다 주석으로 구분을 해야할까?
일반적인 경우라면 주석 없이 빈 줄로 준비, 실행, 검증 구절 구분 가능
하지만 대규모 테스트의 경우 준비 단계 안에서도 설정을 구분하는 등의 경우가 발생 가능
따라서 준비 및 검증 구절 안에 빈 줄을 추가해야 하는 테스트라면 주석 유지할 것
테스트 픽스처 재사용법
테스트 픽스처를 준비하기 위해 많은 코드 필요. 따라서 별도의 메서드나 클래스로 도출한 후 테스트 간에 재사용하는 것이 좋음.
그럼 어떻게 하면 잘 재사용할 수 있을까?
사전 셋업 구문 X: @BeforeEach
@BeforeEach
여러 테스트에서 공통 부분이 있다고 사전 셋업 구문에 추가하지 말자.
테스트 간 결합도 높아짐
테스트 가독성 떨어짐
다만 예외로 대부분의 테스트에 사용되는 경우 가능
예) 데이터베이스 연결 코드
하지만 그 때도 base class에서 설정하고 그걸 상속하는 게 더 합리적
테스트 클래스에 private 팩토리 메서드 두기
단위 테스트 명명법
규칙을 따르지 말 것.
예를 들어 [테스트 대상 메서드_시나리오_예상결과] 같은 정해진 이름 규칙
복잡한 동작에 대한 높은 수준의 설명을 이런 규칙에 넣을 수 없음. 비개발자에게 시나리오를 설명하는 것처럼 테스트 이름을 짓자.
참고
테스트 클래스 이름 지정시 [클래스명]Test를 쓰긴 하지만, 그 클래스만 검증한다는 게 아니라, 어딘가에서 시작해야 하므로 진입점, API 정도를 나타내는 것.
매개변수화된 테스트: parameterized test
동작을 설명하기에 한 테스트는 보통 충분하지 않다. 이를 위해 단위 테스트 프레임워크에선 매개변수화된 테스트를 제공
예를 들어 날짜를 테스트한다면, 성공/실패 케이스 중에서도 성공하는 여러 날짜 종류와 실패하는 여러 날짜 종류가 있을 수 있다.
하지만 이제 테스트 메서드가 나타내는 사실을 파악하기가 어려워진다.
따라서 절충안으로 긍정적인 테스트 케이스는 고유한 테스트로 도출하고, 가장 중요한 부분을 잘 설명하는 이름을 쓰면 좋다. 그리고 부정적인 테스트를 여러 매개변수로 넘길 수 있다.
하지만 동작이 너무 복잡하면 사용하지 말고 모두 각각 고유의 테스트 메서드로 나타내자.
Last updated