자바 백엔드 입문 52편. Phase 7 Testing 시작. @SpringBootTest로 Spring 컨테이너를 띄우고 실제 Bean들로 통합 테스트하는 표준 패턴을 모의 비행 시뮬레이터 비유로 풀어쓴 학습 노트.
이 글은 자바 백엔드 입문 시리즈 59편 중 52편이에요. Phase 6.5 JPA 4편이 끝났고, 이번 52편부터 Phase 7 Testing 2편으로 들어갑니다. 코드를 짜는 것만큼 중요한 "내가 짠 게 진짜 동작하는가 자동 검증" 의 표준 패턴.
통합 테스트가 어렵게 들리는 이유
처음 "Spring Boot 테스트" 를 검색하면 — @SpringBootTest·@WebMvcTest·@DataJpaTest·@MockBean·@Autowired 같은 어노테이션이 한꺼번에 쏟아져요. 무엇을 언제 써야 할지 안 잡혀요.
이 글에서는 모의 비행 시뮬레이터 비유로 풀어요. 단위 테스트 = "비행기 한 부품만 따로 테스트", 통합 테스트 = "전체 비행기를 시뮬레이터에 띄워 실제 비행 시험". @SpringBootTest 가 그 시뮬레이터예요. 끝까지 따라오시면 5종 테스트 어노테이션이 한 그림에 들어와요.
테스트 의존성 — 자동 포함
Spring Boot 프로젝트는 spring-boot-starter-test 가 자동 포함돼요. 이 한 줄에 다음이 다 들어 있어요.
- JUnit 5 — 테스트 프레임워크
- AssertJ — 검증 표현 라이브러리
- Mockito — 모의 객체 라이브러리
- Spring Test — Spring 전용 테스트 지원
- Spring Boot Test — Spring Boot 전용 테스트 지원
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
별도 설정 없이 위 5개 도구가 자동 동작 준비.
첫 통합 테스트 — @SpringBootTest
가장 단순한 통합 테스트 골격.
@SpringBootTest
class OrderServiceIntegrationTest {
@Autowired
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
@Test
void 주문_생성_후_조회() {
// Given
OrderRequest request = new OrderRequest(1L, 10000, "Alice");
// When
Order saved = orderService.create(request);
Order found = orderRepository.findById(saved.getId()).get();
// Then
assertThat(found.getAmount()).isEqualTo(10000);
assertThat(found.getStatus()).isEqualTo("PENDING");
}
}
이 테스트가 실행되면 — @SpringBootTest 가 실제 Spring 컨테이너를 띄움. @Autowired 로 진짜 Service·Repository Bean을 받아서 "운영과 똑같은 환경" 에서 동작 검증. 가장 신뢰성 높은 테스트.
테스트 작성 표준 — Given·When·Then
테스트 한 메서드 안에 세 부분 명시.
@Test
void 메서드명() {
// Given — 테스트 사전 조건 세팅
OrderRequest request = ...;
// When — 검증 대상 동작 실행
Order saved = orderService.create(request);
// Then — 결과 검증
assertThat(saved.getId()).isNotNull();
}
세 단어 그대로 주석에 박는 게 한국 회사 표준. 테스트 메서드 이름은 한국어로 "무엇을 검증하는지" 명확히 — 주문_생성_후_상태_확인() 같이.
AssertJ — 검증 표현
표준 JUnit assertEquals(expected, actual) 보다 가독성 좋은 AssertJ.
// JUnit 표준
assertEquals(10000, order.getAmount());
assertTrue(order.getStatus().equals("PENDING"));
// AssertJ (권장)
assertThat(order.getAmount()).isEqualTo(10000);
assertThat(order.getStatus()).isEqualTo("PENDING");
assertThat(order.getCreatedAt()).isAfter(LocalDateTime.now().minusMinutes(1));
assertThat(orderRepository.findAll())
.hasSize(3)
.extracting(Order::getStatus)
.containsExactly("PENDING", "PAID", "SHIPPED");
체이닝으로 검증이 자연스러워요. 메서드 이름이 거의 영어 문장처럼 읽혀요. spring-boot-starter-test 에 자동 포함.
@SpringBootTest의 무게 — 느림
@SpringBootTest 는 실제 Spring 컨테이너를 띄우는 무거운 테스트. 한 번 시동에 1~10초 걸려요. 모든 테스트에 박으면 — 테스트 100개가 10분 넘게 걸릴 수도.
해결 — "Spring 전체가 필요 없는 테스트는 더 가벼운 어노테이션" 사용.
Slice 테스트 — 일부 영역만 띄우기
Spring Boot가 제공하는 "슬라이스(Slice) 테스트" 어노테이션.
| 어노테이션 | 띄우는 영역 | 사용 |
|---|---|---|
@SpringBootTest |
전체 컨테이너 | E2E·통합 |
@WebMvcTest |
Controller·MVC 부품만 | 컨트롤러 단위 |
@DataJpaTest |
JPA Repository + 인메모리 DB | Repository 단위 |
@JsonTest |
Jackson 직렬화만 | JSON 변환 검증 |
@RestClientTest |
RestTemplate·WebClient만 | 외부 API 호출 |
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired private MockMvc mockMvc;
@MockBean private OrderService orderService; // 진짜 Service는 안 띄움
@Test
void 주문_조회() throws Exception {
when(orderService.findById(1L)).thenReturn(new Order(1L, 10000, "PENDING"));
mockMvc.perform(get("/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.amount").value(10000));
}
}
@WebMvcTest 는 "OrderController에 필요한 Web 부품만" 띄워요. Service·Repository는 안 띄워서 — 2~3배 빠름. MockMvc 로 HTTP 요청 흉내 + @MockBean 으로 Service를 가짜 객체로 대체.
@DataJpaTest — Repository 전용
JPA Repository만 테스트하고 싶을 때.
@DataJpaTest
class OrderRepositoryTest {
@Autowired private OrderRepository orderRepository;
@Autowired private TestEntityManager em;
@Test
void 상태별_주문_조회() {
// Given
em.persistAndFlush(new Order(null, 10000, "PENDING"));
em.persistAndFlush(new Order(null, 20000, "PENDING"));
em.persistAndFlush(new Order(null, 30000, "COMPLETED"));
// When
List<Order> pending = orderRepository.findByStatus("PENDING");
// Then
assertThat(pending).hasSize(2);
}
}
@DataJpaTest 가 자동으로 — 인메모리 H2 DB로 교체 + JPA만 띄움. 진짜 PostgreSQL 없이도 빠르게 Repository 테스트. 테스트 후 자동 롤백 도 보너스.
@MockBean — 가짜 Bean으로 교체
특정 의존성을 "진짜 Bean 대신 가짜" 로 바꾸고 싶을 때.
@SpringBootTest
class OrderServiceTest {
@Autowired private OrderService orderService;
@MockBean
private PaymentGateway paymentGateway; // 진짜 결제 게이트웨이 대신 가짜
@Test
void 결제_실패_시_주문_롤백() {
// Given — 가짜 결제 게이트웨이가 예외 던지도록
when(paymentGateway.charge(any())).thenThrow(new PaymentFailedException());
// When + Then
assertThatThrownBy(() -> orderService.placeOrder(new OrderRequest(...)))
.isInstanceOf(PaymentFailedException.class);
}
}
진짜 카카오페이·토스에 안 가고 — 우리가 정의한 동작(예외 던지기·특정 값 반환)으로 가짜 처리. Mockito 의 when(...).thenReturn(...) / when(...).thenThrow(...) 패턴.
@Transactional 테스트 — 자동 롤백
테스트 메서드에 @Transactional 박으면 — 테스트 후 모든 DB 변경 자동 롤백.
@SpringBootTest
@Transactional
class OrderServiceIntegrationTest {
@Test
void test1() {
orderRepository.save(new Order(...)); // INSERT
// ↓ 테스트 끝나면 자동 ROLLBACK
}
@Test
void test2() {
// test1의 영향 없음 — DB 깨끗한 상태
}
}
테스트 간 격리 + DB 오염 방지. @SpringBootTest 와 함께 박는 게 표준 패턴.
테스트 프로파일 — application-test.yml
테스트 전용 설정을 따로 두는 게 표준.
# src/test/resources/application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create-drop
@SpringBootTest
@ActiveProfiles("test") // test 프로파일 활성
class IntegrationTest { ... }
운영 PostgreSQL 대신 — 테스트는 H2 인메모리로. CI 환경에서 DB 없이도 테스트 실행 가능.
전체 흐름 검증 → @SpringBootTest. Controller·HTTP → @WebMvcTest. JPA Repository → @DataJpaTest. 순수 Service 로직 → 어노테이션 X + Mockito 단위 테스트. 가장 가벼운 게 가장 빠름.
한 줄 정리 — @SpringBootTest = 전체 Spring 컨테이너 띄우는 통합 테스트. 무거운 만큼 신뢰성 높음. 가벼운 슬라이스 테스트(@WebMvcTest·@DataJpaTest)와 조합. @MockBean 으로 외부 의존성 대체, @Transactional 로 자동 롤백.
시험 직전 한 번 더 — 통합 테스트 입문자가 매번 헷갈리는 것
spring-boot-starter-test한 줄 = JUnit 5·AssertJ·Mockito·Spring Test 다 포함@SpringBootTest= 전체 Spring 컨테이너 띄우는 통합 테스트- 무거움 (1~10초 시동) — 모든 테스트에 박지 X
- 슬라이스 테스트 = 일부 영역만 띄워 빠르게
@WebMvcTest= Controller·MVC만@DataJpaTest= JPA Repository + 인메모리 H2@JsonTest= Jackson 직렬화만@RestClientTest= REST 클라이언트만- 표준 패턴 = Given·When·Then 주석
- 메서드 이름 = 한국어로 "무엇을 검증" 명확히
- AssertJ =
assertThat(...).isEqualTo(...)체이닝 MockMvc= HTTP 요청 흉내 (실제 Tomcat 안 띄움)@MockBean= 진짜 Bean 대신 가짜- Mockito =
when(...).thenReturn(...)/when(...).thenThrow(...) - 가짜 객체로 외부 의존성(결제·이메일) 차단
@Transactional테스트 = 자동 롤백- 테스트 간 격리 + DB 오염 방지
@ActiveProfiles("test")= 테스트 전용 설정 활성application-test.yml= 테스트 DB·캐시 설정 분리- 인메모리 H2 = 운영 PostgreSQL 대체 빠른 테스트
- 가장 빠른 단위 테스트 = Spring 어노테이션 X + Mockito만
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 47편 — JPA @Embedded @Embeddable 값 객체
- 48편 — JPA Auditing @CreatedDate @LastModifiedDate
- 49편 — JPA 메서드 이름 쿼리 @Query
- 50편 — QueryDSL 타입 안전 동적 쿼리
- 51편 — 영속성 컨텍스트와 LazyLoading
다음 글: