상세 버전
- spring boot 3.2.0
- security 6.2.0
상황, 원인 분석
security 추가하면서 잘 돌아가던 @WebMvcTest가 시원하게 403 Forbidden 에러 출력하였다
@WebMvcTest(value = MemberController.class)
class MemberControllerWebMvcTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private MemberUseCase memberUseCase;
@Test
void 회원가입_성공하면_아이디값을_반환한다() throws Exception {
given(memberUseCase.create(any()))
.willReturn(6L);
MemberCreate memberCreate = new MemberCreate("tester", "123456", "테스터", "123456-1234567");
mockMvc.perform(post("/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(memberCreate))
).andDo(print())
.andExpectAll(
status().isOk(),
jsonPath("$.status").value("SUCCESS"),
jsonPath("$.id").value(6)
);
}
}
문서를 읽어보니
@WebMvcTest 사용할 경우 아래 bean들이 기본 스캔되고,
MockMvc와 Spring Security 자동 구성 설정도 되는 것으로 보였다
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not @Component, @Service or @Repository beans).
By default, tests annotated with @WebMvcTest will also auto-configure Spring Security and MockMvc (include support for HtmlUnit WebClient and Selenium WebDriver). For more fine-grained control of MockMVC the @AutoConfigureMockMvc annotation can be used.
참고. 직접 커스텀한 SecurityFilterChain Bean은 디버깅시 반응 없는것으로 확인
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final AuthenticationConfiguration authenticationConfiguration;
private final ObjectMapper objectMapper;
private final JwtUtil jwtUtil;
public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, ObjectMapper objectMapper, JwtUtil jwtUtil) {
this.authenticationConfiguration = authenticationConfiguration;
this.objectMapper = objectMapper;
this.jwtUtil = jwtUtil;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable);
//.. 생략
return http.build();
}
// .. 생략
}
(원인파악) SpringBootWebSecurityConfiguration 클래스에서 SecurityFilterChain이 기본 등록되는 것으로 확인되었다.
더군다나 requests.anyRequest().authenticated()설정되다보니 모든 요청에 대해 인증이 필요하여 기존 @WebMvcTest이 실패했던거였다
해결
@WebMvcTest의 경우 @DataJpaTest와 같이 특정 계층에 대한 테스트를 가볍게 하기 위해 지원하는 것으로 이해하고 있다.
그런데 개인적으로 "security와 같이 서블릿 필터부터 시작되는 보안 관심사가 @WebMvcTest와 같은 경량화 Controller 테스트에 필요할까"라는 생각이 들었다.
그래서 Controller 테스트에 집중할 수 있도록 @TestConfiguration로 필요한 최소 설정하는 방식으로 해결하였다.
(security 커스텀 설정에 대한 내용은 통합 테스트에서 확인하는 걸로~)
@TestConfiguration
public class TestSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(auth ->
auth.requestMatchers("/signup", "/login").permitAll()
// 그외 기타 설정
.anyRequest().authenticated());
return http.build();
}
}
@WebMvcTest(value = MemberController.class)
@Import(TestSecurityConfig.class) // 추가
class MemberControllerWebMvcTest {
//..
}
+추가. Controller 테스트에서 SecurityContext 정보가 필요한 경우
@WithMockUser, @WithAnonymousUser 등등 어노테이션 활용해서 처리할 수 있다
// 예시
@WithMockUser(username = "tester", password = "123456", role = "USER")
@Test
void xx결과값을_응답한다() throws Exception {
given(refundUseCase.getAmount(anyString()))
.willReturn(BigDecimal.valueOf(1005000));
mockMvc.perform(get("/refund"))
.andDo(print())
.andExpectAll(
status().isOk(),
jsonPath("$.금액").value("1,005,000")
);
}
참고. 우아한 형제
https://tecoble.techcourse.co.kr/post/2020-09-30-spring-security-test/
'공부 > Spring' 카테고리의 다른 글
[Spring Boot] Simple Cache, EhCache(v3.10.8) 간단 테스트 해보기 (0) | 2024.07.22 |
---|---|
[Spring] AOP 용어 정리 (0) | 2023.08.22 |
[JPA] Date 타입 포맷 맞춰주는 @Temporal (0) | 2022.06.20 |
[JUnit] org.junit.runners.model.InvalidTestClassError: Invalid test class (0) | 2022.06.19 |
[ERROR] org.springframework.oxm.UncategorizedMappingException: Unknown JAXB exception (0) | 2022.06.12 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!