스택큐힙리스트

자바 스프링 개발 시작하기 - 13일차 테스트 주도 개발 본문

개발

자바 스프링 개발 시작하기 - 13일차 테스트 주도 개발

스택큐힙리스트 2025. 7. 20. 18:58
반응형

1. TDD가 왜 중요한가?

Red → Green → Refactor” 사이클을 통해 서비스 레이어의 요구사항을 코드로 먼저 검증하면, 새 기능을 추가해도 회귀 버그가 줄고 구조가 깔끔해집니다. 테스트가 있는 코드베이스는 리팩터링과 CI/CD에 강합니다.

2. JUnit 5 빠르게 세팅하기

Gradle 기준:

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
    testImplementation 'org.mockito:mockito-junit-jupiter:5.12.0'
    testImplementation 'org.testcontainers:junit-jupiter:1.19.7'
}
test {
    useJUnitPlatform()
}

Tip : 스프링 부트 3.x는 기본으로 JUnit 5 의존성을 포함하지만, 버전 충돌을 막기 위해 testImplementation에 명시하는 편이 안전합니다.

3. Mockito로 서비스 레이어 단위 테스트

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock private UserRepository userRepository;
    @InjectMocks private UserService userService;

    @Test
    void createUser_success() {
        UserSaveRequest req = new UserSaveRequest("jaemin", "jaemin@mail.com");
        when(userRepository.save(any())).thenReturn(req.toEntity());

        User result = userService.createUser(req);

        assertAll(
            () -> assertEquals("jaemin", result.getName()),
            () -> verify(userRepository).save(any(User.class))
        );
    }
}
  • Mock 객체로 외부 의존성을 격리 → 진짜 DB 없이도 로직 검증
  • BDDMockito의 given-when-then 스타일을 쓰면 시나리오가 더 읽기 쉽습니다.

4. Testcontainers로 통합 테스트까지 확장

@Testcontainers
@SpringBootTest
class OrderServiceIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres =
        new PostgreSQLContainer<>("postgres:16-alpine")
              .withDatabaseName("testdb");

    @DynamicPropertySource
    static void overrideProps(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired OrderService orderService;

    @Test
    void placeOrder_success() {
        // given
        OrderRequest req = new OrderRequest(...);

        // when
        Order saved = orderService.placeOrder(req);

        // then
        assertThat(saved.getId()).isNotNull();
    }
}
  • 실제 컨테이너 기반 DB로 스프링 컨텍스트 전체를 검증
  • 깃허브 Actions + services: 옵션으로 CI 상에서도 동일한 환경을 재현 가능

5. 유지 보수를 위한 실전 팁

  1. 테스트 네이밍 컨벤션: 메서드명_시나리오_기대결과
  2. Fixture 재사용: @TestConfiguration 또는 Fixture 클래스로 중복 제거
  3. 커버리지 목표: 단위 테스트 > 70 %, 통합 테스트 > 20 %부터 시작해 점진적 확대
  4. 테스트 실행 속도: 통합 테스트는 @Tag("integration")로 구분해 로컬/CI에서 선택적 실행
반응형
Comments