본문 바로가기
1인개발자에서 살아남는법!/시리즈

[#7 spring boot] 테스트 및 오류 처리 전략

by Alan__kang__morlang 2025. 6. 5.
반응형

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 인메모리 (테스트 실행 시 자동 구성)

반응형