diff --git a/spring-transition-plan/test_reference.md b/spring-transition-plan/test_reference.md new file mode 100644 index 0000000..0648d58 --- /dev/null +++ b/spring-transition-plan/test_reference.md @@ -0,0 +1,159 @@ +# 스프링 전환 테스트 참조 문서 + +## 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 테스트 +```java +@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 테스트 +```java +@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 found = userRepository.findByEmail("test@example.com"); + + // Then + assertThat(found).isPresent(); + } +} +``` + +### 2.3 Controller 테스트 +```java +@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 +```java +@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 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 패턴 적용