[Testing Strategy] JUnit5와 Mockito: 지속 가능한 단위 테스트를 위한 레이어드 아키텍처
소프트웨어의 품질은 코드의 양이 아니라 테스트의 질로 결정된다. 특히 MSA나 복잡한 레이어드 아키텍처 환경에서 **단위 테스트(Unit Test)**는 단순히 버그를 찾는 도구를 넘어, 설계의 결함을 드러내는 리트머스 시험지 역할을 한다. JUnit5와 Mockito를 결합하여 견고한 테스트 환경을 구축하기 위한 핵심 팩트와 아키텍처 전략을 분석한다.
1. JUnit5: 현대적 테스트의 파운데이션
JUnit5는 이전 버전과 달리 JUnit Platform, JUnit Jupiter, JUnit Vintage의 모듈 구조로 재설계되었다. 아키텍트 입장에서 주목해야 할 변화는 **확장 모델(Extension Model)**이다.
Lifecycle Control:
@BeforeEach,@AfterEach를 통해 각 테스트 간 격리(Isolation)를 보장한다.Display Name:
@DisplayName을 활용하여 비즈니스 요구사항 중심의 테스트 가독성을 확보한다.Conditional Execution: 환경 변수나 OS에 따라 테스트 실행 여부를 동적으로 결정할 수 있다.
2. Mockito: 의존성 분리의 핵심 기술
단위 테스트의 성패는 **'관심사의 분리'**에 있다. 테스트 대상(SUT, System Under Test)이 의존하는 객체를 실제 객체가 아닌 가짜 객체(Mock)로 대체함으로써, 오직 해당 로직의 정합성만 검증한다.
Stubbing:
when(...).thenReturn(...)을 통해 의존 객체의 행위를 정의한다.Verification:
verify(...)를 통해 특정 메서드가 예상대로 호출되었는지, 호출 횟수는 정확한지 검증한다.Spying: 실제 객체의 로직을 유지하면서 특정 메서드만 모킹하고 싶을 때 사용한다.
3. 레이어별 테스트 아키텍처 전략
① Web Layer (Controller)
컨트롤러 테스트는 HTTP 요청/응답 형식과 입력값 검증(Validation)에 집중한다. 서비스 레이어는 모두 모킹하여 네트워크나 DB 개입을 차단한다.
Tool:
MockMvc를 활용하여 서블릿 컨테이너를 띄우지 않고도 빠른 테스트를 수행한다.
② Service Layer (Business Logic)
비즈니스 로직의 핵심이다. Repository나 외부 API 연동 부문은 Mockito로 철저히 격리한다.
Fact: 서비스 테스트에서 실제 DB를 연결하는 순간 그것은 단위 테스트가 아닌 통합 테스트가 된다. Mockito의
@Mock과@InjectMocks를 활용해 순수 자바 로직만 검증하라.
③ Persistence Layer (Repository)
DB 쿼리의 정확성을 검증한다. 여기서는 예외적으로 모킹 대신 인메모리 DB(H2)나 Testcontainers를 활용한 실제 DB 연동 테스트를 권장한다.
4. 실전 적용 시 반드시 지켜야 할 3계명
① 테스트를 위한 코드를 생산하지 마라
Private 메서드를 테스트하기 위해 접근 제어자를 바꾸는 것은 설계가 잘못되었다는 신호다. 해당 로직을 별도 클래스로 분리하거나 public 메서드를 통해 간접적으로 검증하라.
② BDD(Behavior Driven Development) 스타일 적용
Given-When-Then 구조를 유지하라. 테스트 코드는 그 자체로 기술 명세서가 되어야 한다.
// Given: 준비 (Mock 설정)
when(memberRepository.findById(1L)).thenReturn(Optional.of(member));
// When: 실행 (테스트 대상 호출)
MemberResponse response = memberService.getMember(1L);
// Then: 검증 (결과 확인)
assertThat(response.getName()).isEqualTo("JamJam");
③ Mocking의 함정에 빠지지 마라
모든 것을 모킹하면 테스트 코드는 통과하지만 실제 운영 환경에서는 깨지는 '테스트의 허구'에 갇힐 수 있다. 복잡한 도메인 모델은 가급적 실제 객체(POJO)를 사용하고, 외부 시스템 연동부(IO) 위주로 모킹하라.
5. 아키텍트의 결론
단위 테스트는 개발 속도를 늦추는 장애물이 아니라, 리팩토링을 가능하게 하는 안전망이다. JUnit5의 강력한 생태계와 Mockito의 유연함을 결합하여 "작성하기 쉽고, 읽기 쉬우며, 믿을 수 있는" 테스트 스위트를 구축하는 것이 아키텍트의 가장 중요한 책무 중 하나다.
댓글
댓글 쓰기