6장 - 단위 테스트 스타일
6장에서 다루는 내용
- 단위 테스트 스타일 비교
- 함수형 아키텍처와 육각형 아키텍처의 관계
- 출력 기반 테스트로 전환
단위 테스트 스타일
3가지 테스트 스타일이 존재합니다.
.
출력 기반 테스트 - 가장 품질 좋음
상태 기반 테스트- 두 번째로 좋은 선택
통신 기반 테스트- 간헐적 사용
출력 기반 테스트
테스트 대상 시스템(SUT)에 입력을 넣으면 생성되는 출력을 점검하는 방식입니다.
상품할인을 계산하는 클래스가 존재한다고 가정했을 때 상품을 넣으면 할인된 가격이 나와 해당 가격을 검증할 수 있습니다.
여기서 중요한점은 내부 컬렉션에 상품을 추가하거나, 데이터베이스에 저장하지 않습니다.
이 스타일을 함수형 스타일이라고도 하며 부작용이 없는 함수입니다.
상태 기반 테스트
상태기반은 클라이언트가 상품을 추가했을 때, Products 컬렉션을 검증하는 방식이 있습니다.
Order클래스의 내부 상태를 테스트해야 하기 때문에 상태 기반 테스트라고 부릅니다.
통신 기반 테스트
통신 기반 테스트는 Mock을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증합니다.
예를 들어 메일을 보내는 클래스를 Mocking 해서 해당 행위가 일어났는지 검증합니다.
회귀 방지와 피드백 속도로 3가지 테스트 비교
Mock이 런타임에 지연 시간을 주진 하지만 수만 개 수준이 아니라면 별로 차이는 없어서 비슷비슷하다.
리팩터링 내성 지표로 3가지 테스트 비교
출력 기반 테스트는 내부 상태를 검증하지 않기 때문에 리팩터링 내성이 가장 우수하다.
상태 기반 테스트는 내부 상태를 검증하기 때문에 일반적으로 거짓 양성이 되기 쉽습니다.
통신 기반 테스트는 상호 작용을 확인하고, 구현세부사항과 관계가 깊기 때문에 리팩터링 내성이 가장 좋지 않습니다.
유지보수성 지표로 3가지 테스트 비교
유지보수성의 지표
- 테스트가 이해하기 얼마나 어려운가
- 테스트가 실행하기 얼마나 어려운가
출력 기반 테스트가 가장 짧고 간결하기 때문에 유지보수하기 쉽다.
상태 기반 테스트의 경우에는 종종 많은 데이터를 확인해야 할 수 있어 출력 기반 테스트보다 더 많은 공간을 차지한다.
통신 기반 테스트는 Mock을 관리해야하고 해당 Mock이 다른 Mock을 필요로 하는 경우에는 테스트가 더 커지고 유지보수하기 어려워집니다.
함수형 아키텍처 이해
출력 기반 단위 테스트 스타일을 함수형이라고도 합니다.
함수형 프로그래밍이란 순수 함수를 사용한 프로그래밍으로, 호출 횟수에 상관없이 주어진 입력에 대해 동일한 출력을 생성합니다.
f(x) = x+1을 예시로 보면 항상 호출 횟수에 상관없이 주어진 입력에 대해 동일한 출력을 생성합니다.
이런 함수들은 테스트가 짧고 간결하며 유지보수하기 쉽습니다.
반대로 인스턴스의 상태를 변경하거나 디스크, 파일을 업데이트하는 부작용, 메서드가 예외를 던지는 경우, DateTime.now()와 같이 현재 시간을 가져오는 경우가 존재하면 테스트가 힘들어집니다.
물론 어떤 부작용도 일으키지 않은 애플리케이션을 만들 수는 없지만, 함수형 프로그래밍의 목표는 부작용을 완전히 제거하는 것이 아니라 비즈니스 로직과 부작용을 일으키는 코드를 분리하는 것입니다.
테스트하기 힘든 부분과, 테스트하기 쉬운 부분을 분리하는 세미나가 생각납니다.
www.youtube.com/watch?v=Cz_a2gQp63c
함수형 아키텍처와 육각형 아키텍처
육각형 아키텍처는 도메인 계층과 애플리케이션 서비스 계층을 분리합니다.
도메인 계층은 비즈니스 로직을 처리하는 책임을 가지고, 서비스 계층은 데이터베이스 SMTP 서비스와 같이 외부 애플리케이션과의 통신에 책임이 있습니다.
결정과 실행을 분리하는 함수형 아키텍처와 매우 유사합니다.
함수형 아키텍처와 출력 기반 테스트로의 전환
- 프로세스 외부 의존성에서 목으로 변경
- 목에서 함수형 아키텍처로 변경
모든 방문자를 추적하는 감사 시스템으로, 가장 최근 파일의 마지막줄에 방문자의 이름과 방문 시간을 추가합니다.
파일당 최대 항목 수에 도달하면 인덱스를 증가시켜 새 파일에 작성합니다.
시스템의 초기 버전은 AuditManager클래스에서 AddRecord 메서드를 통해 모든 작업을 수행합니다.
파일 시스템과 밀접하게 연결되어 있어 AuditManager는 테스트하기 어려운 코드입니다.
파일과 관련된 모든 연산을 별도의 클래로 도출하고 해당 클래스를 AuditManager에서 클래스로 주입합니다.
이제 파일과 관련된 연산은 Mock으로 처리할 수 있습니다.
하지만 여기에 한번더 발전시켜 Mock을 제거하고 함수형 아키텍처로 변경할 수 있습니다.
비즈니스 로직만 따로 로직으로 추출하여 부작용에 대한 명령만 반환하도록 합니다.
함수형 아키텍처의 단점 이해하기
코드베이스가 커지고, 성능에 영향을 미칠 수 있습니다.
기존에는 하나의 클래스, 하나의 메서드였다면 테스트하기 어려운 영역과 쉬운 영역을 분리하면서 여러 개의 클래스들이 생길 수 있습니다.
또한 파일을 기록하기 전에 접근권한에 따라 DB에 질의해야 하는 조건이 추가된다면 접근권한이 필요 없는 경우에도 무조건 데이터베이스에 질의를 해야 할 수도 있으며, 성능과 유지보수성 사이에서 트레이드오프를 적합하게 고려해야 합니다.