Spring Boot Unit 테스트 작성. JUnit5+Mockito, domain 패키지별 구조, entity는 common/entity, service는 {domain}/service. 네이밍 test_{대상}{시나리오}{예상결과}. Entity 직접 반환 금지 (DTO만)...
app/backend/{service}/src/test/java/
├── {domain}/
│ └── service/
│ └── UserServiceTest.java # 도메인별 Service 테스트
└── common/
├── entity/
│ └── UserTest.java # Entity 단위 테스트
└── repository/
└── UserRepositoryTest.java # Repository 테스트
형식: test_{대상}_{시나리오}_{예상결과}
// ✅ Good
@Test
void test_createUser_withValidData_returnsUserDto() { }
@Test
void test_createUser_withDuplicateEmail_throwsException() { }
// ❌ Bad
@Test
void test1() { }
@Test
void testCreateUser() { } // 시나리오/결과 누락
@Test
void test_findById_withExistingId_returnsUser() {
// Arrange (준비)
User user = new User("test@example.com");
when(repository.findById(1L)).thenReturn(Optional.of(user));
// Act (실행)
UserDto result = service.findById(1L);
// Assert (검증)
assertThat(result.getEmail()).isEqualTo("test@example.com");
verify(repository, times(1)).findById(1L);
}
핵심: 예상치 못한 모든 결과는 실패로 처리. 검증 생략 금지.
@Test
void test_createUser_bad() {
UserDto result = service.createUser(request);
// ID만 확인하고 다른 필드는 검증 안함
assertThat(result.getId()).isNotNull();
}
@Test
void test_createUser_withValidData_returnsCompleteUserDto() {
// Arrange
CreateUserRequest request = new CreateUserRequest("test@example.com", "Test User");
User savedUser = new User(1L, "test@example.com", "Test User");
when(repository.save(any(User.class))).thenReturn(savedUser);
// Act
UserDto result = service.createUser(request);
// Assert - 모든 필드 검증
assertThat(result).isNotNull();
assertThat(result.getId()).isEqualTo(1L);
assertThat(result.getEmail()).isEqualTo("test@example.com");
assertThat(result.getName()).isEqualTo("Test User");
// Mock 호출 검증
verify(repository, times(1)).save(any(User.class));
verifyNoMoreInteractions(repository);
}
@Test
void test_createUser_withDuplicateEmail_bad() {
// 예외만 발생하면 통과
assertThrows(DuplicateEmailException.class, () -> {
service.createUser(request);
});
}
@Test
void test_createUser_withDuplicateEmail_throwsDetailedException() {
// Arrange
CreateUserRequest request = new CreateUserRequest("duplicate@example.com", "Test");
when(repository.existsByEmail("duplicate@example.com")).thenReturn(true);
// Act & Assert
DuplicateEmailException exception = assertThrows(
DuplicateEmailException.class,
() -> service.createUser(request)
);
// 예외 메시지 검증
assertThat(exception.getMessage()).contains("duplicate@example.com");
assertThat(exception.getMessage()).contains("already exists");
// 부작용 검증 (DB 저장 시도하지 않았는지)
verify(repository, times(1)).existsByEmail("duplicate@example.com");
verify(repository, never()).save(any(User.class));
verifyNoMoreInteractions(repository);
}
verify(), times(), verifyNoMoreInteractions()참고: 상세한 예제는 docs/design/testing-strategy.md의 "엄격한 검증 원칙" 섹션 참조
@Test, Mockito @Mock, @InjectMocks 사용test_{대상}_{시나리오}_{예상결과}verifyNoMoreInteractions() 사용하여 예상치 못한 호출 감지cd app/backend/{service} && ./gradlew test# 전체 서비스 테스트
cd app/backend/user-service && ./gradlew test
# 특정 테스트 클래스만
./gradlew test --tests UserServiceTest
# 특정 메서드만
./gradlew test --tests UserServiceTest.test_createUser_withValidData_returnsUserDto