테스트 가이드
기본 원칙
- 테스트 없는 코드는 완성된 코드가 아니다
- 단위 테스트(Unit Test)와 통합 테스트(Integration Test) 구분
- 테스트 코드도 프로덕션 코드와 동일한 품질 기준 적용
- Given-When-Then 패턴 사용
- 테스트 Coverage 목표: 80% 이상
테스트 클래스 어노테이션
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("unitTest")
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
// ...
}
| 어노테이션 | 설명 |
|---|---|
@SpringBootTest | 전체 Spring Context 로드 |
@AutoConfigureMockMvc | MockMvc 자동 설정 |
@ActiveProfiles("unitTest") | unitTest 프로파일 활성화 |
MockMvc + ObjectMapper 사용
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("unitTest")
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("사용자 등록 성공")
void regist_success() throws Exception {
// Given
UserVo vo = new UserVo();
vo.setUserId("user01");
vo.setUserNm("홍길동");
vo.setUserEmail("hong@example.com");
// When & Then
mockMvc.perform(post("/api/user/regist")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(vo)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true));
}
@Test
@DisplayName("사용자 목록 조회 성공")
void getList_success() throws Exception {
// Given
UserVo vo = new UserVo();
vo.setRowPerPage(10);
vo.setOffset(0);
// When & Then
mockMvc.perform(post("/api/user/getList")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(vo)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data").isArray());
}
}
테스트 메서드 네이밍
형식: 메서드명_상황_기대결과
@Test
@DisplayName("사용자 등록 성공 시 200 반환")
void regist_success_returns200() throws Exception { ... }
@Test
@DisplayName("필수값 누락 시 400 반환")
void regist_missingRequiredField_returns400() throws Exception { ... }
@Test
@DisplayName("아이디 중복 시 에러 응답")
void regist_duplicateUserId_returnsError() throws Exception { ... }
단위 테스트 (Service)
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserMapper userMapper;
@InjectMocks
private UserService userService;
@Test
@DisplayName("사용자 조회 성공")
void get_existingUser_returnsUser() {
// Given
UserVo vo = new UserVo();
vo.setUserSn(1L);
UserVo mockUser = new UserVo();
mockUser.setUserSn(1L);
mockUser.setUserNm("홍길동");
given(userMapper.get(vo)).willReturn(mockUser);
// When
UserVo result = userService.get(vo);
// Then
assertThat(result).isNotNull();
assertThat(result.getUserNm()).isEqualTo("홍길동");
}
@Test
@DisplayName("존재하지 않는 사용자 조회 시 예외 발생")
void get_nonExistingUser_throwsException() {
// Given
UserVo vo = new UserVo();
vo.setUserSn(999L);
given(userMapper.get(vo)).willReturn(null);
// When & Then
assertThatThrownBy(() -> userService.get(vo))
.isInstanceOf(BusinessException.class);
}
}
JsonPath 표현식 예시
// 최상위 필드 검증
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("등록되었습니다."))
// 배열 검증
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data").isEmpty())
.andExpect(jsonPath("$.data", hasSize(3)))
// 중첩 필드 검증
.andExpect(jsonPath("$.data.userId").value("user01"))
.andExpect(jsonPath("$.data.userNm").value("홍길동"))
// 배열 요소 검증
.andExpect(jsonPath("$.data[0].userId").value("user01"))
.andExpect(jsonPath("$.data[0].userNm").value("홍길동"))
Hamcrest Matchers 예시
import static org.hamcrest.Matchers.*;
// 크기 검증
.andExpect(jsonPath("$.data", hasSize(5)))
// null 검증
.andExpect(jsonPath("$.data").value(IsNull.notNullValue()))
// 포함 검증
.andExpect(jsonPath("$.data[*].userId", hasItem("user01")))
// 범위 검증
.andExpect(jsonPath("$.totalCount").value(greaterThan(0)))
테스트 설정 파일
# src/test/resources/application-unitTest.yml
spring:
datasource:
url: jdbc:h2:mem:testdb;MODE=Oracle
driver-class-name: org.h2.Driver
sql:
init:
mode: always
schema-locations: classpath:test-schema.sql
AssertJ 사용 (권장)
// assertThat (AssertJ) — 가독성 높음
assertThat(result).isNotNull();
assertThat(result).hasSize(3);
assertThat(result.getUserNm()).isEqualTo("홍길동");
assertThatThrownBy(() -> service.get(vo))
.isInstanceOf(BusinessException.class)
.hasMessageContaining("찾을 수 없습니다");
테스트 작성 체크리스트
- [ ]
@SpringBootTest,@AutoConfigureMockMvc,@ActiveProfiles("unitTest")사용 - [ ]
MockMvc+ObjectMapper주입 - [ ] 테스트 메서드명:
메서드명_상황_기대결과형식 - [ ] Given-When-Then 구조 준수
- [ ]
@DisplayName으로 테스트 목적 명시 - [ ] JsonPath로 응답 검증
- [ ] 경계 조건(null, 빈 값, 최대값) 테스트 포함
- [ ] Coverage 목표: 80% 이상