하위 태스크 1
테스트 의존성 확인
spring-boot-starter-test의존성 확인
build.gradle 파일의 dependencies 영역에 테스트 관련 의존성을 추가한다.
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}하위 태스크 2 ~ 3
통합 테스트 클래스 생성
@SpringBootTest+@AutoConfigureMockMvc테스트 클래스 생성
기본 GET API 테스트
/members등 엔드포인트에 대한 200 OK 테스트 작성
MemberController 클래스를 테스트하는 MemberControllerTests 클래스를 작성한다.
@SpringBootTest: 클래스가 테스트에 포함되고 내부 메서드 중에@Test어노테이션이 추가된 메서드가 실행된다.@AutoConfigureMockMvc: MockMvc를 활성화한다. MockMvc는 WAS를 모방하여 스필링 컨테이너를 만들고 테스트를 위해 REST API를 호출한다.
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("사용자 컨트롤러 테스트")
public class MemberControllerTests {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("사용자 생성 테스트")
public void create() throws Exception {
MemberRequest memberRequest = MemberRequest.builder()
.name("홍길동")
.email("[email protected]")
.age(32)
.build();
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/api/members")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(memberRequest));
// 200 OK 테스트
MvcResult mvcResult = mockMvc.perform(requestBuilder)
.andExpect(status().is2xxSuccessful())
.andReturn();
MemberResponse memberResponse = objectMapper
.readValue(mvcResult.getResponse().getContentAsByteArray(), MemberResponse.class);
// 응답 데이터 테스트
assertThat(memberResponse).isNotNull();
assertThat(memberResponse.getId()).isGreaterThan(0);
assertThat(memberResponse.getName()).isEqualTo("홍길동");
}
}
하위 태스크 4 ~ 5
404/400 에러 테스트
잘못된 요청 시 404/400을 기대하는 테스트 추가
BDD 스타일 네이밍
Given-When-Then 구조의 테스트 메서드 이름/주석 작성
build.gradle 파일에 유효성 검증을 위한 의존성을 추가한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
+ implementation 'org.springframework.boot:spring-boot-starter-validation'
// ...
}MemberRequest 클래스와 MemberController 클래스의 멤버에 유효성 검증 어노테이션을 추가한다.
MemberRequest.java:
public class MemberRequest {
+ @NotBlank
private String name;
+ @NotBlank
+ @Email
private String email;
+ @Min(0)
private Integer age;
}MemberController.java:
public class MemberController {
// ...
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
- public MemberResponse post(@RequestBody MemberRequest memberRequest) {
+ public MemberResponse post(@Valid @RequestBody MemberRequest memberRequest) {
return memberService.create(memberRequest);
}
// ...
}
MemberControllerTests 클래스에 잘못된 요청 시 404, 400을 기대하는 테스트를 추가한다.
public class MemberControllerTests {
// ...
+ @Test
+ @DisplayName("존재하지 않는 ID가 존재하면 404 반환")
+ public void getNotFoundMember() throws Exception {
+ // Given
+ Long testId = -1L;
+
+ // When
+ RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/members/{id}", testId);
+
+ // Then
+ mockMvc.perform(requestBuilder)
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ @DisplayName("유효성 검증에 실패하면 400 반환")
+ public void createBadRequestMember() throws Exception {
+ // Given
+ MemberRequest testRequest = MemberRequest.builder()
+ .name("")
+ .email("It is not a email.")
+ .age(-1)
+ .build();
+
+ // When
+ RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/api/members")
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content(objectMapper.writeValueAsString(testRequest));
+
+ // Then
+ mockMvc.perform(requestBuilder)
+ .andExpect(status().isBadRequest());
+ }
}MemberControllerTests 테스트를 실행한 결과는 다음과 같다.

하위 태스크 6
LoggingFilter 구현
OncePerRequestFilter기반 로깅 필터 구현
OncePerRequestFilter 클래스를 상속받는 LoggingFilter 클래스를 생성한다.
@WebFilter(urlPatterns = "/api/*")
@Slf4j
public class LoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.info("요청: {}, {}", request.getMethod(), request.getRequestURI());
filterChain.doFilter(request, response);
log.info("응답: {}", response.getStatus());
}
}하위 태스크 7
FilterRegistrationBean 등록
필터 Bean 등록 및 URL 패턴/순서 지정
필터를 원하는 대로 제어하기 위한 FilterConfig 클래스를 생성한다.
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(1);
return registrationBean;
}
}하위 태스크 8
필터 로그 검증
API 호출 시 필터 로그가 의도대로 출력되는지 확인
MemberControllerTests 테스트를 수행한다. 로깅 필터가 동작하여 테스트 로그에 기록된 것을 확인할 수 있다.
