07. 아키텍처 요소 테스트하기
테스트 피라미드
- 단위 테스트가 가장 높은 커버리지를 유지해야 한다.
- 만드는 비용이 적고 유지보수하기 쉽고 빨리 실행되고 안정적이기 때문
- 통합 테스트로 넘어가면 테스트 비용이 비싸지고 실행이 느려지고 깨지기 쉬워진다.
- 테스트 피라미드는 테스트가 비싸질수록 커버리지 목표를 낮게 잡아야 한다는 것을 보여준다.
- 그렇지 않으면 기능 구현보다 테스트를 만드는 데 시간을 더 쓰게 된다.
- 단위 테스트는 일반적으로 하나의 클래스를 인스턴스화하고 해당 클래스의 인터페이스를 통해 테스트한다.
- 통합 테스트는 여러 유닛을 인스턴스화하여 시작점이 되는 클래스의 인터페이스로 테스트한다.
- 유닛들의 네트워크를 검증
- 여러 계층 간 경계를 걸쳐 테스트하기에 때로는 목을 사용할 수도 있다.
- 시스템 테스트는 애플리케이션을 구성하는 모든 객체 네트워크를 가동시켜 특정 유스케이스가 전 계층에서 잘 동작하는지 검증한다.
단위 테스트로 도메인 엔티티 테스트하기
- 도메인 엔티티를 대상으로 단위 테스트를 진행할 수 있다.
- 단위 테스트는 만들기 쉽고 빠르게 실행된다.
- 도메인 엔티티의 행동은 다른 클래스에 거의 의존하지 않기에 다른 종류의 테스트는 필요하지 않다.
단위 테스트로 유스케이스 테스트하기
- 유스케이스를 테스트하는 것도 단위 테스트라고 할 수도 있다.
- 의존성의 상호작용을 테스트하고 있기에 통합 테스트라고도 볼 수 있지만 목으로 작업하고 있기에 단위 테스트라고도 볼 수 있다.
- SendMoneyServiceTest 예제 코드
- 테스트 중인 유스케이스는 상태가 없기에 모킹된 의존 대상과의 상호작용 여부를 테스트한다. (행위 검증)
- 행위 검증을 하게 되면 리팩터링 시 테스트가 깨지기 쉬워지기 때문에 모든 동작이 아닌 핵심 동작만 골라 테스트하는 것이 좋다.
통합 테스트로 웹 어댑터 테스트하기
- 웹 어댑터는 HTTP를 통해 JSON 등을 입력 받고 입력 유효성을 검증한 뒤 유스케이스에서 사용할 수 있는 포맷으로 매핑한 뒤 유스케이스에 전달한다.
- SendMoneyControllerTest 예제 코드
- 웹 어댑터의 책임 대부분은 이런 테스트로 커버된다.
@WebMvcTest
는 HTTP 입력 검증, 자바와 JSON 간 매핑 등의 객체 네트워크를 인스턴스화하도록 만들고 이 네트워크의 일부로서 컨트롤러가 잘 동작하는지 테스트한다. → 통합 테스트
- 만약 컨트롤러를 단위 테스트로 만들면 모든 매핑, 유효성 검증, HTTP 항목에 관한 커버리지가 낮아진다.
- 애초에 컨트롤러는 스프링 프레임워크와 결합이 강하기 때문에 통합 테스트로 만드는 것이 합리적이다.
통합 테스트로 영속성 어댑터 테스트하기
- 영속성 어댑터 테스트도 데이터베이스와의 매핑을 검증하기 때문에 통합 테스트로 하는 것이 합리적이다.
- AccountPersistenceAdapterTest 예제 코드
- 영속성 어댑터 테스트에선 목을 사용하지 않고 실제 데이터베이스와 연동한다.
- 스프링에서는 기본적으로 인메모리 데이터베이스를 테스트에서 사용하는데 설정이 필요 없어 실용적이다.
- 다만 실제 운영 환경과 다르기 때문에 예상치 못한 문제가 발생할 수 있다. (SQL 문법 차이 등)
- Testcontainers 같은 라이브러리를 통해 실제 DB를 도커에 띄어 테스트 환경을 구축할 수도 있다.
시스템 테스트로 주요 경로 테스트하기
- 시스템 테스트는 전체 애플리케이션을 띄우고 API를 통해 요청을 보내 모든 계층을 테스트한다.
- AccountSystemTest 예제 코드
- 시스템 테스트라고 해도 언제나 서드파티 시스템을 이용할 수 있는 것은 아니기에 결국 모킹을 할 때도 있다.
- 몇 가지 출력 포트 인터페이스만 모킹하면 된다.
- 시스템 테스트를 통해 애플리케이션이 배포될 준비가 됐다는 확신을 가질 수 있다.
얼마만큼의 테스트가 충분할까?
- 라인 커버리지는 중요하지 않다.
- 코드의 중요한 부분이 커버되지 않았을 수도 있기 때문
- 얼마나 마음 편하게 소프트웨어를 배포할 수 있느냐를 테스트의 성공 기준으로 삼으면 된다.
- 프로덕션의 버그를 수정하고 이로부터 배우는 것을 우선순위로 삼으면 된다.
- 육각형 아키텍처에서 사용하는 전략
- 도메인 엔티티와 유스케이스는 단위 테스트로 커버
- 어댑터는 통합 테스트로 커버
- 사용자가 취할 수 있는 중요 애플리케이션 경로는 시스템 테스트로 커버
- 리팩터링할 때마다 테스트를 과하게 변경해야 한다면 설계가 잘못된 것이다.