반응형
Spring Boot 테스트 전략 완전 정복 – 단위, JPA, 통합 테스트까지
주니어 개발자에게 실무에서 가장 중요한 역량 중 하나는 테스트 작성 능력입니다.
이 글에서는 Spring Boot 프로젝트에서 자주 쓰는 테스트 종류부터 설정법, 예제 코드까지 차근차근 설명드릴게요.
테스트 종류 요약
종류 | 설명 | 도구 |
---|---|---|
단위 테스트 | 서비스/유틸 등 비즈니스 로직 단위 검증 | JUnit5 + Mockito |
JPA 테스트 | JpaRepository 쿼리 및 DB 연동 검증 |
@DataJpaTest + H2 |
통합 테스트 | Controller ↔ Service ↔ DB 전체 흐름 검증 | @SpringBootTest + MockMvc |
예외 처리 테스트 | 입력값 오류 등 실패 케이스 처리 검증 | @SpringBootTest + MockMvc |
Gradle 의존성 설정
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.springframework.security:spring-security-test' // 보안 사용 시
}
1️⃣ 단위 테스트 (Unit Test)
서비스나 유틸리티 메서드를 테스트할 때 가장 기본적인 형태입니다.
@SpringBootTest
class RecipeServiceTest {
@MockBean
private RecipeRepository recipeRepository;
@Autowired
private RecipeService recipeService;
@Test
void 레시피_생성_성공() {
Recipe recipe = new Recipe();
recipe.setRecipeName("비빔밥");
when(recipeRepository.save(any(Recipe.class))).thenReturn(recipe);
Recipe saved = recipeService.save(recipe);
assertThat(saved.getRecipeName()).isEqualTo("비빔밥");
}
}
---
2️⃣ JPA Repository 테스트
Repository의 쿼리 동작 여부를 빠르게 검증할 수 있어요.
@DataJpaTest
class RecipeRepositoryTest {
@Autowired
private RecipeRepository recipeRepository;
@Test
void 레시피_조회() {
Recipe recipe = new Recipe();
recipe.setRecipeName("된장찌개");
recipeRepository.save(recipe);
List<Recipe> result = recipeRepository.findByRecipeName("된장찌개");
assertThat(result).hasSize(1);
}
}
3️⃣ 통합 테스트 (Integration Test)
전체 플로우(Controller → Service → Repository)를 검증할 때 사용합니다.
MockMvc를 사용하는 방식
@SpringBootTest
@AutoConfigureMockMvc
class RecipeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void 레시피_등록_페이지_호출() throws Exception {
mockMvc.perform(get("/recipe/form"))
.andExpect(status().isOk())
.andExpect(view().name("recipe/recipeForm"));
}
}
TestRestTemplate 사용
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RecipeApiTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void 레시피_API_저장() {
RecipeFormDto dto = new RecipeFormDto();
dto.setRecipeName("김치전");
ResponseEntity<String> response =
restTemplate.postForEntity("/recipe/save", dto, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}
4️⃣ 예외 처리 테스트
입력값 오류 또는 비정상 요청에 대해 정상적인 오류 응답을 보내는지 확인합니다.
@Test
void 레시피명_없을때_400에러_확인() throws Exception {
mockMvc.perform(post("/recipe/save")
.param("recipeName", ""))
.andExpect(status().isBadRequest());
}
정리: 어떤 테스트를 언제 사용하면 좋을까?
- 로직만 검증하고 싶다면 → 단위 테스트
- DB 쿼리가 잘 동작하는지 확인 → JPA 테스트
- 전체 요청-응답 흐름 확인 → 통합 테스트
- 사용자 입력 검증 → 예외 처리 테스트
단위 테스트와 통합 테스트를 진행하다 보면, 실제 데이터베이스와 분리된 테스트 환경이 필요합니다.
이 글에서는 H2 인메모리 DB를 사용하고, application-test.yml로 테스트 전용 설정을 분리하는 방법을 알려드릴게요.
테스트용 DB가 필요한 이유
- 운영/로컬 DB를 직접 건드리지 않음
- CI 환경(GitHub Actions 등)에서 가볍게 실행 가능
- 테스트 데이터는 실행할 때마다 초기화됨 → 결과 예측 가능
1️⃣ H2 의존성 추가
dependencies {
testImplementation 'com.h2database:h2'
}
---
2️⃣ 테스트 전용 application 설정 파일 생성
src/main/resources/application-test.yml
파일 생성
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MYSQL
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
설명:
ddl-auto: create-drop
→ 테스트마다 DB 테이블 새로 생성/삭제MODE=MYSQL
→ H2를 MySQL처럼 동작시킴 (호환성 증가)
---
3️⃣ 테스트 실행 시 profile 지정
테스트 클래스에 아래 어노테이션 추가:
@ActiveProfiles("test")
@SpringBootTest
class RecipeServiceTest {
...
}
이렇게 하면 `application-test.yml` 설정이 적용됩니다.
---
4️⃣ 테스트 중에만 사용할 전용 Bean 등록 (선택)
@TestConfiguration
을 통해 테스트에서만 사용할 MockBean
이나 FakeBean
을 만들 수 있어요.
@TestConfiguration
public class TestMockConfig {
@Bean
public MyExternalService mockExternalService() {
return Mockito.mock(MyExternalService.class);
}
}
---
5️⃣ 테스트 DB에서 쿼리 확인 (H2 콘솔 사용)
H2 콘솔 활성화: application-test.yml
에서 h2.console.enabled: true
URL 접속: http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:testdb
User Name: sa
Password: (비워두기)
참고: H2 테스트 실행 시 발생할 수 있는 이슈
- MySQL 고유 문법 오류 →
MODE=MYSQL
설정으로 해결 - LONGTEXT, MEDIUMTEXT 등 지원 안됨 →
@Lob
+columnDefinition
으로 대체 - Foreign key 오류 → 테스트 DB는 외래키 무시하는 것이 더 유리할 수 있음
마무리
이제부터는 테스트 시 운영 DB에 영향 주지 않고 가볍고 빠르게 실행 가능한 환경을 갖출 수 있게 되었어요!
- 로컬 DB: MariaDB, MySQL, PostgreSQL
- 테스트 DB: H2 인메모리 (테스트 실행 시 자동 구성)
반응형
'1인개발자에서 살아남는법! > 시리즈' 카테고리의 다른 글
[바이브 코딩]앱을 바로 만들 수 있다고? 바이브 코딩으로 앱 수익화 도전기 (1) (0) | 2025.07.06 |
---|---|
[#08 Spring boot] JWT vs 세션, Spring Security에서 둘 다 사용하는 통합 전략 (0) | 2025.06.16 |
[#6 Spring boot] Spring Boot + Ajax 파일 업로드 가이드 (0) | 2025.06.02 |
[#5 spring boot] 기능 구현 예제 적용 – 레시피 게시판 CRUD 및 Thymeleaf 연동 (0) | 2025.06.01 |
[#4 spring boot] MyBatis 기반 프로젝트, JPA + Thymeleaf로 전환하는 모든 방법 (Spring Boot) (0) | 2025.05.31 |