초기 기능 구현 시, 테스트 코드를 함께 작성하면 기능이 적을 때는 얼마 걸리지 않던 부분이 개발이 진행되고 기능이 누적됨에 따라 테스트 코드도 함께 누적되고 의존성도 복잡해지며 테스트 자체의 시간이 오래 걸리는 경우를 확인할 수 있다. 돌이켜보면 분명 초 단위로 금방 끝나던 테스트들이 2~3분을 넘기게 되고 점점 기능이 개발됨에 따라 계속해서 테스트 시간이 늘어감을 확인할 수 있다.
물론, 정말 필요한 테스트가 오래 걸릴 수는 있지만, 테스트 시간은 동일 커버리지라면 짧을 수록 더 좋다고 생각한다. 내가 주로 진행한 프로젝트 테스트 코드의 대부분이 통합 테스트 기반이기 때문에 테스트 컨테이너.. DB 연동.. 등의 과정이 들어가며 불필요한 시간 소모가 많다고 생각했고, 리팩토링을 통해 단위테스트로 분리해서 이를 최적화할 수 있다는 생각을 하게 되었다.
테스트 피라미드에 따르면 단위 테스트가 가장 아래에 있어 가장 많은 테스트 부분을 차지해야 한다는 것을 확인할 수 있지만, 현재 대부분이 통합 테스트인 내 테스트 코드는 올바르지 않은 방향이라는 것을 알 수 있었다.

통합 테스트 vs 단위 테스트
우선, 현재 작성된 통합테스트 중에서도 정말 통합테스트로서의 가치가 있고 필요한 테스트가 존재할 수 있기 때문에 두 개의 정확히 파악하는 것이 중요하다고 생각했다.
단위 테스트(Unit Test)
개별 클래스 및 메서드의 동작을 독립적으로 검증하는 테스트입니다. 테스트 대상의 크기가 정해져 있지는 않지만, 일반적으로 가장 작은 단위의 기능을 검증한다.
단위 테스트 특징
- 빠른 실행 속도: 외부 의존성을 배제하여 빠르게 실행됩니다.
- 독립성: Mock 객체나 Stub을 사용해 외부 의존성을 대체합니다.
- 순수한 로직 검증: 비즈니스 로직 자체에만 집중할 수 있습니다.
하지만 실제 의존성을 연결하지 않기 때문에 전체적인 동작이나 실제 환경과의 상호작용은 검증할 수 없는 모습을 보인다.
통합테스트(Integration Test)
여러 컴포넌트나 모듈이 함께 동작하는지를 검증하는 테스트입니다. 실제 의존성을 주입받아 실제 환경과 유사한 조건에서 테스트를 진행한다.
통합 테스트 특징
- 실제 환경 시뮬레이션: 데이터베이스, 외부 API 등과의 실제 연동을 테스트합니다
- 전체 시나리오 검증: 사용자의 실제 사용 흐름을 검증할 수 있습니다
- 높은 신뢰성: 실제 운영 환경에서의 동작을 보다 정확히 예측할 수 있습니다
하지만, Spring Boot 기준으로 애플리케이션 컨텍스트를 로드하는 시간이 소요되기 때문에, 단위 테스트에 비해 실행 시간이 오래 걸리게 된다.
내가 작성했던 테스트들을 기준으로 생각했을 때

전체 테스트가 45초 905ms가 걸렷는데, 단순히 단위 테스트로 작성된 소셜 로그인 관련 테스트는 ms인 반면, TestContainer을 사용하고 DB를 사용하는 통합 테스트의 경우 절반 수준인 24초 866ms가 걸린 모습을 확인할 수 있다.
이는 로컬에서 실행시켰기 때문에 비교적 빠른 시간이었지만 실제 CI 파이프라인을 통한 테스트에서는 캐싱이 없을 때 거의 4분 이상이 소요되는 모습을 확인할 수 있었다.
그렇다면 어떤 부분을 단위 테스트로 작성할 지, 어떤 부분을 통합 테스트로 작성할 지를 잘 파악해서 구분해야 커버리지도 챙기면서 최적의 테스트 시간을 가져가 시간적인 비용을 줄일 수 있을 것이다.
위의 특징들을 바탕으로 생각해봤을 때, 외부 의존성의 영향을 받지 않는 검증 부분, 순수 비즈니스 로직 같은 부분을 통합 테스트에서 단위 테스트로 변경할 수 있을 것 같다. 그리고 하나의 도메인에 1개의 테스트 클래스를 작성하며 너무 긴 테스트 코드가 작성되게 되는데 도메인 내부 계층과 클래스 단위로 분리를 해서 단위 테스트를 작성하면 더 적합하지 않을까 생각이 들었다.
예를 들면 현재 통합 테스트로 작성된 부분에서 필터 조건에 따라 결과가 달라지는 로직이 존재해 각 필터별로 request -> controller -> usecase -> service -> db -> response 까지의 모든 과정을 거치는 테스트로 작성되어 있다. 이를 Controller / Service / Repository 등의 계층 별로 단위 테스트로 구분해서 작성하는 것이 더 옳은 방향일까? 라는 고민을 하게 되었다. 아마 이 부분은 실제로 리팩토링을 진행해보며 비교를 하는 과정에서 확실하게 알 수 있지 않을까 생각된다.
단위 테스트 - FIRST 원칙
클린 코드 책에 의하면, 깨끗한 단위 테스트를 위해 5가지 규칙을 적용해야한다고 한다.
- Fast : 테스트는 빠르게 실행되어야 한다.
- Independent : 각 테스트는 서로 의존하지 않고 독립적으로 실행되어야 한다.
- Repeatable : 환경에 관계없이 같은 결과를 반복적으로 얻을 수 있어야 한다.
- Self-validating : 테스트는 bool 값으로 결과를 내야 한다. 즉 성공 또는 실패로 명확한 결과를 얻을 수 있어야 한다.
- Timely : 단위 테스트 코드는 테스트하려는 실제 코드를 구현하기 직전에 작성되어야 한다.
TDD 와 관련된 내용이다 보니 Timely 같은 부분이 존재하는데, 아직 테스트 코드에 익숙하지 않은 나는 실제 코드를 구현하고 이를 바탕으로 테스트 코드를 작성하게 되는 습관이 존재한다.. 좀 더 열심히 테스트 코드 공부를 하면 이러한 습관을 역전시킬 수 있을 것 같다는 생각도 들게 되었다.
앞으로 진행할 테스트 코드 리팩토링에 관해 목표는 다음과 같다
- Jacoco 테스트 커버리지 기준 70% 이상 달성(라인/조건 커버리지 선택은 논의가 필요할 것 같다)
- 전체 테스트 코드 실행 시간 40% 이상 단축
테스트 커버리지로 테스트 코드의 신뢰성을 가져가면서 시간 단축까지 이루는 것이 현재의 내 목표라고 볼 수 있다.
테스트 코드 작성을 했을 때 가장 뿌듯하게 느껴진 순간(번외)

내가 작성한 테스트 코드로 인해, 코드를 리팩토링 하는 과정에서 변경점에 대해 안정성을 검증할 수 있었다는 리뷰를 받게 되었을 때 가장 기뻤다 ㅎㅎ.. 계층별 슬라이스 테스트를 통해 속도를 가져가는 리팩토링을 얼른 해야겠다..
'Back-End' 카테고리의 다른 글
| @Transactional(readOnly=true)는 왜 써야하는걸까? (0) | 2025.09.18 |
|---|---|
| [JPA] 외래키 주인으로 알아보는 엔티티 연관관계 (0) | 2025.09.17 |
| 프로젝트 성능 개선기(nGrinder을 활용한 부하 테스트) (0) | 2025.04.02 |
| 트래픽이 많아지는 상황에서 나는 어떻게 대처할 수 있을까? (0) | 2025.02.17 |
| Mysql 비효율적인 쿼리 개선기(+ 무한스크롤, 페이징 성능 비교) (0) | 2025.01.09 |