runtime-smoke-20260710143823/spring-transition-plan/test_reference.md

4.7 KiB

스프링 전환 테스트 참조 문서

1. 테스트 전략 개요

1.1 테스트 피라미드

           ▲
          /E2E\        (End-to-End)
         /──────\
        /Integration\  (Integration)
       /────────────\
      /  Unit Tests  \ (Unit)
     /────────────────\

1.2 목표 커버리지

테스트 유형 목표 도구
단위 테스트 80% JUnit 5, Mockito
통합 테스트 70% Spring Test, TestContainers
E2E 테스트 50% RestAssured

2. 단위 테스트 기준

2.1 Service Layer 테스트

@SpringBootTest
class UserServiceTest {
    @Autowired private UserService userService;
    @MockBean private UserRepository userRepository;

    @Test
    void shouldCreateUserSuccessfully() {
        // Given
        UserDto dto = new UserDto("test@example.com", "password");
        when(userRepository.save(any(User.class))).thenAnswer(i -> i.getArgument(0));

        // When
        User result = userService.createUser(dto);

        // Then
        assertThat(result.getEmail()).isEqualTo("test@example.com");
        verify(userRepository).save(any(User.class));
    }

    @Test
    void shouldThrowExceptionForDuplicateEmail() {
        // Given
        String email = "existing@example.com";
        when(userRepository.findByEmail(email)).thenReturn(Optional.of(new User()));

        // When/Then
        assertThatThrownBy(() -> userService.createUser(new UserDto(email, "pass")))
            .isInstanceOf(DuplicateEmailException.class);
    }
}

2.2 Repository 테스트

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserRepositoryTest {
    @Autowired private UserRepository userRepository;
    @Autowired private TestEntityManager entityManager;

    @Test
    void shouldFindByEmail() {
        // Given
        User user = new User("test@example.com", "encoded");
        entityManager.persist(user);

        // When
        Optional<User> found = userRepository.findByEmail("test@example.com");

        // Then
        assertThat(found).isPresent();
    }
}

2.3 Controller 테스트

@WebMvcTest(UserController.class)
class UserControllerTest {
    @Autowired private MockMvc mockMvc;
    @MockBean private UserService userService;

    @Test
    void shouldCreateUser() throws Exception {
        // Given
        CreateUserRequest request = new CreateUserRequest("test@example.com", "password");
        when(userService.createUser(any())).thenReturn(new User(1L, "test@example.com"));

        // When/Then
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.email").value("test@example.com"));
    }
}

3. 통합 테스트 기준

3.1 Database Integration Test

@SpringBootTest
@Testcontainers
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserRepositoryIntegrationTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

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

    @Test
    void shouldPersistAndRetrieveUser() {
        User user = new User("integration@test.com", "password");
        User saved = userRepository.save(user);
        Optional<User> found = userRepository.findById(saved.getId());
        assertThat(found).isPresent();
    }
}

4. 테스트 실행 기준

테스트 유형 실행 시점 실패 시 동작
단위 테스트 매 커밋 빌드 실패
통합 테스트 PR 생성 빌드 실패
E2E 테스트 일 1회 알림 발송
성능 테스트 주 1회 보고

5. 검증 체크리스트

전환 완료 기준

  • 단위 테스트 커버리지 80% 이상
  • 모든 Service 메서드 테스트 coverage
  • 모든 Repository 쿼리 테스트 coverage
  • 모든 Controller endpoint 테스트 coverage
  • Integration Test Database 환경 독립적 실행
  • Mock 객체 주입 검증
  • 예외 처리 경로 테스트 coverage
  • 테스트 격리 확인

코드 품질 기준

  • SonarQube Quality Gate 통과
  • Cyclomatic Complexity < 10
  • 테스트 명명 규칙 준수
  • Given-When-Then 패턴 적용