이 가이드는 MockMvc와 @WebMvcTest를 사용하여 컨트롤러 계층을 테스트하는 표준 방법을 정의합니다.
특히 우리 프로젝트의 ResponseAspect(HTTP Status 변환)와 Security 인증 처리를 올바르게 적용하는 데 중점을 둡니다.
@SpringBootTest 대신 **@WebMvcTest*를 사용하여 컨트롤러와 관련된 빈(Bean)만 가볍게 로드합니다.ResponseDto를 처리하는 Aspect가 동작해야 하므로 @Import(ResponseAspect.class) 가 필수입니다.모든 컨트롤러 테스트는 아래 템플릿을 기반으로 작성합니다.
package com.bugzero.rarego.domain.in; // 패키지명 예시
// 1. Static Imports (필수)
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; // csrf(), authentication()
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockitoBean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
// [Core Imports] 우리 프로젝트 클래스
import com.bugzero.rarego.global.aspect.ResponseAspect;
import com.bugzero.rarego.global.security.MemberPrincipal;
// ... DTOs & Facade
// 2. 클래스 레벨 설정
@WebMvcTest(TargetController.class) // 테스트할 컨트롤러 지정
@Import(ResponseAspect.class) // ★ 핵심: Status 변환 AOP 활성화
@EnableAspectJAutoProxy // AOP 프록시 활성화
class TargetControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private TargetFacade targetFacade; // Facade/Service Mocking
@Autowired
private ObjectMapper objectMapper;
// 3. 인증 객체 생성 Helper 메서드 (복사해서 사용)
private Authentication createAuth(String publicId, String role) {
MemberPrincipal principal = new MemberPrincipal(publicId, role);
return new UsernamePasswordAuthenticationToken(
principal,
null,
List.of(new SimpleGrantedAuthority("ROLE_" + role))
);
}
// 4. 테스트 케이스 작성...
}
csrf()가 필요 없습니다.param) 매핑을 확인합니다.@Test
@DisplayName("성공: 조회 요청 시 200 OK 반환")
void read_success() throws Exception {
// given
String memberId = "user-1";
given(targetFacade.readSomething(eq(memberId))).willReturn(responseDto);
// when & then
mockMvc.perform(get("/api/v1/resource")
.param("page", "0")
.with(authentication(createAuth(memberId, "USER")))) // 인증 주입
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.id").value(1L));
}
.with(csrf()) 가 반드시 필요합니다. (없으면 403 Forbidden 발생)objectMapper로 JSON 변환하여 전송합니다.@Test
@DisplayName("성공: 생성 요청 시 201 Created 반환")
void create_success() throws Exception {
// given
RequestDto request = new RequestDto("data");
given(targetFacade.create(any())).willReturn(responseDto);
// when & then
mockMvc.perform(post("/api/v1/resource")
.with(csrf()) // ★ 필수
.with(authentication(createAuth("admin", "ADMIN")))
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.status").value(201));
}
ResponseAspect가 정상 동작하는지 검증하는 핵심 테스트입니다.