![[Spring] MockMvc 테스트에서 with(csrf()) 자동화 하기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9bzNl%2FbtsJ1D7oyaz%2FkyH6VYieetqEYLkIfOw9vk%2Fimg.png)
[Spring] MockMvc 테스트에서 with(csrf()) 자동화 하기테스트 코드2024. 10. 11. 09:29
Table of Contents
1. 사건 발달
스프링 시큐리티 환경에서 MVC를 테스트하기 위해서는 요청 헤더에 CSRF 토큰이 필요하다. 따라서 아래 코드와 같이 perform() 시에 요청 헤더에 스프링 시큐리티가 생성한 CSRF 토큰을 포함할 수 있다.
근데 테스트 코드를 작성할 때마다 CSRF 토큰을 일일이 추가하는 것보단, 테스트 실행 전에 자동으로 추가할 수 있지 않을까?
@WithMockUser
@WebMvcTest(controllers = SectionController.class)
class SectionControllerTest {
@Test
@DisplayName("신규 회고카드를 등록한다.")
void createSection() throws Exception {
//given
CreateSectionRequest request = CreateSectionRequest.builder()
.retrospectiveId(1L)
.templateSectionId(2L)
.sectionContent("내용")
.build();
//when //then
mockMvc.perform(
post("/sections")
.content(objectMapper.writeValueAsString(request))
.contentType(APPLICATION_JSON)
.with(csrf())
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.code").value("201"))
.andExpect(jsonPath("$.message").doesNotExist());
}
}
2. 요청 헤더에 CSRF 토큰 자동으로 추가하기
@BeforeEach는 JUnit5에서 제공하는 애너테이션으로, 각 테스트가 실행되기 전에 호출된다. 이 애너테이션을 사용하여 모든 테스트가 실행되기 전에 CSRF 토큰을 추가하도록 해보자.
- (1) MockMvcBuilders는 MockMvc 객체를 생성할 때 사용되는 유틸리티 클래스이다.
- (2) 실제 웹 애플리케이션 컨텍스트(WebApplicationContext)를 사용하여 MockMvc 객체를 생성한다.
- WebApplicationContext는 테스트할 때, Spring의 실제 웹 애플리케이션 환경을 모방하기 위해 사용된다.
- (3) Spring Security 설정을 MockMvc에 적용한다.
- springSecurity()는 SecurityMockMvcConfigurers의 메서드로, Spring Security의 필터 체인이 MockMvc 요청에 대해 적용되도록 설정한다.
- 실제 웹 애플리케이션에서처럼 인증, 인가, CSRF 보호 등의 보안 기능을 테스트할 수 있게 된다.
- (4) POST 요청의 모든 엔드포인트에 대하여 CSRF 토큰을 자동으로 포함한다.
- defaultRequest()는 모든 테스트에서 사용될 요청 정보를 설정한다.
- post(), get() 이런 식으로 HTTP 메서드를 지정할 수 있다.
- ex) post("/**") : 모든 경로("/**")에 대해 POST 요청을 대상으로 설정을 수행한다.
@WithMockUser
@WebMvcTest(controllers = SectionController.class)
class SectionControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private WebApplicationContext applicationContext;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders // (1)
.webAppContextSetup(applicationContext) // (2)
.apply(springSecurity()) // (3)
.defaultRequest(post("/**").with(csrf())) // (4)
.build();
}
}
3. 결론
@BeforeEach에서 MockMvc 설정을 통해 매번 테스트 코드에 with(csrf())를 수동으로 추가하는 번거로움을 줄일 수 있다.
@WithMockUser
@WebMvcTest(controllers = SectionController.class)
class SectionControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private WebApplicationContext applicationContext;
@Autowired
private ObjectMapper objectMapper;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders
.webAppContextSetup(applicationContext)
.apply(springSecurity())
.defaultRequest(post("/**").with(csrf()))
.build();
}
@Test
@DisplayName("신규 회고카드를 등록한다.")
void createSection() throws Exception {
//given
CreateSectionRequest request = CreateSectionRequest.builder()
.retrospectiveId(1L)
.templateSectionId(2L)
.sectionContent("내용")
.build();
//when //then
mockMvc.perform(
post("/sections")
.content(objectMapper.writeValueAsString(request))
.contentType(APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.code").value("201"))
.andExpect(jsonPath("$.message").doesNotExist());
}
}