단위 테스트와 통합 테스트를 나누는 기준
테스트 코드는 변경 사항이 기존 동작을 깨뜨렸는지 빠르게 알려주는 안전장치다. 하지만 모든 테스트를 Spring 컨텍스트로 실행하면 피드백이 느려지고, 반대로 모든 의존성을 mock으로 대체하면 실제 설정과 데이터베이스의 문제를 놓칠 수 있다. 단위 테스트와 통합 테스트는 우열이 아니라 확인하려는 경계가 다르다.
두 테스트의 범위
| 구분 | 확인 대상 | 실행 속도 | 실패가 알려 주는 것 |
|---|---|---|---|
| 단위 테스트 | 한 클래스나 작은 모듈의 규칙 | 빠름 | 순수한 입력·출력과 분기 로직의 오류 |
| 통합 테스트 | 여러 컴포넌트와 인프라의 연결 | 상대적으로 느림 | 빈 등록, 직렬화, SQL, 트랜잭션 경계의 오류 |
단위 테스트에서는 외부 의존성을 대역으로 바꾼다. 서비스가 저장소에 어떤 인자를 전달하는지, 잘못된 입력에 어떤 예외를 던지는지처럼 도메인 규칙을 빠르게 반복한다.
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock OrderRepository orderRepository;
@InjectMocks OrderService orderService;
@Test
void 이미_주문된_상품은_다시_주문할_수_없다() {
given(orderRepository.existsByUserIdAndProductId(1L, 2L))
.willReturn(true);
assertThatThrownBy(() -> orderService.create(1L, 2L))
.isInstanceOf(AlreadyOrderedException.class);
then(orderRepository).should(never()).save(any());
}
}
이 테스트는 DB가 실제로 연결되는지 확인하지 않는다. 대신 주문 중복이라는 규칙과 저장 호출 여부를 빠르게 검증한다.
통합 테스트가 필요한 지점
다음 경계는 실제 설정을 올려 확인할 가치가 있다.
- Spring 빈이 올바르게 조립되는가
@Transactional과 예외 전파가 의도대로 동작하는가- JPA 매핑과 제약조건이 실제 DB에서 통과하는가
- JSON 요청과 응답의 필드·상태 코드가 계약과 같은가
- Redis, 메시지 브로커, 외부 클라이언트의 설정이 연결되는가
통합 테스트에서 모든 외부 시스템을 매번 호출할 필요는 없다. Testcontainers로 실제와 가까운 DB를 띄우거나, 외부 결제·메일 API는 계약 테스트와 mock 서버로 분리한다. 테스트의 목적이 설정 검증인지 비즈니스 규칙 검증인지 먼저 적으면 범위가 자연스럽게 결정된다.
Spring 컨텍스트 비용 줄이기
@SpringBootTest는 애플리케이션 전체를 로딩하므로 가장 강력하지만 가장 느린 선택이다. 웹 계층은 @WebMvcTest, JPA 저장소는 @DataJpaTest처럼 슬라이스 테스트로 필요한 부분만 올릴 수 있다. 단위 테스트는 Spring을 전혀 띄우지 않아야 한다.
통합 테스트가 느려졌다면 테스트 수를 무조건 줄이기보다 다음을 측정한다.
- 컨텍스트 시작 시간
- 테스트별 DB 초기화 시간
- 외부 의존성 대기 시간
- 실패 재현에 필요한 최소 데이터
동일한 컨텍스트를 공유하면 빨라질 수 있지만, 테스트 간 데이터 격리가 약해지면 순서에 따라 결과가 달라진다. 빠른 실행과 독립성 사이의 기준을 팀 규칙으로 남긴다.
테스트 이름과 구조
테스트 메서드 이름은 구현 함수보다 시나리오를 설명해야 한다. @Nested로 기능별 맥락을 묶고, Given, When, Then 구조를 일정하게 유지하면 실패 로그만 보고도 원인을 찾기 쉽다. 하나의 테스트에서 너무 많은 규칙을 확인하면 어느 조건이 깨졌는지 알기 어렵다.
커버리지 숫자는 참고 지표다. 분기를 실행했다고 해서 잘못된 상태 코드나 동시성 문제까지 검증된 것은 아니다. 중요한 것은 사용자의 핵심 흐름과 실패 경계를 테스트로 표현했는지다.
선택 체크리스트
- 계산·변환·검증 규칙인가? 단위 테스트부터 작성한다.
- 빈 등록·직렬화·트랜잭션·SQL 문제인가? 통합 테스트를 추가한다.
- 외부 시스템의 계약을 확인해야 하는가? 계약 테스트나 mock 서버를 사용한다.
- 실패 테스트가 실제 사용자에게 보이는 상태 코드까지 확인하는가?
- 테스트가 실행 순서와 외부 환경에 의존하지 않는가?
좋은 테스트 전략은 단위 테스트를 많이 만드는 것이 아니라, 각 테스트가 어떤 경계를 보증하는지 분명하게 하는 것이다. 빠른 단위 테스트로 규칙을 촘촘히 확인하고, 핵심 연결 지점에 통합 테스트를 배치하면 수정 속도와 배포 신뢰도를 함께 높일 수 있다.
함께 읽기: