🍃Spring
[Spring] WebMvcTest에서 발생하는 SpringSecurity 의존성 문제 해결하기
waveofmymind
2023. 4. 19. 11:42
AS-IS
UserController의 테스트를 해보기 위해
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = UserController.class,
를 사용해서 테스트 코드를 작성하던 중, 다음과 같은 예외가 발생했다.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '~~' defined in file [~~]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '~~' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
이는, WebMvcTest가 MVC에 초점을 맞춰서 스프링 빈으로 가져오기 때문에, @Service, @Controller, @Component 등이 해당된다.
이 때, Spring Security 관련 구성도 가져오기 때문에,
나의 경우는 Security Config에서 사용하는 JwtAuthorizationFilter, 그리고 다시 필터에서 사용하는 JwtVerifier에 대해 예외가 발생했다.
즉, Security 관련 컴포넌트에서 의존하는 빈들이 @WebMvcTest에 걸리지 않으면 발생하는 예외인 것이다.
해결 과정
나는 Spring Security를 테스트 하는 것이 아닌 UserController에 대해 테스트 하는 것이기 때문에, @WebMvcTest에 걸리는 SecurityConfig와, 관련 JwtVerifier, JwtAuthorizationFilter도 제외시켰다.
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = UserController.class,
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JwtAuthorizationFilter.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JwtVerifier.class)
})
public class UserControllerTest {...}
그리고, 다시 결과를 확인해보니
401 에러가 발생했다.
이는 Securirty의 인증 필터에 의해 인증이 되지 않기 때문에 발생하는 권한이 없다는 예외이다.
그러므로 @WithMockUser를 이용해서 인증을 시도해야한다.
@DisplayName("로그인 API 테스트")
@Test
@WithMockUser
void givenRequest_whenLogin_then200() throws Exception {...}
@WithMockUser vs @WithMockAuthUser
@WithMockUser: 테스트 시 지정된 사용자 세부 정보(사용자 이름, 비밀번호, 권한 등)로 인증된 사용자를 시뮬레이션할 수 있다. 예를 들어, 특정 사용자 이름과 권한을 가진 사용자가 인증된 것처럼 시뮬레이션하기 위해 사용한다.
@WithMockAuthUser: 애플리케이션에 특화된 사용자 세부 정보를 인증 객체에 삽입하는 데 사용한다. 그러나 이를 사용하기위해서는 WithSecurityContextFactory와 같은 사용자 세부 정보를 제공하는 클래스를 구현해야한다.
정리하면, 후자는 설정이 필요하지만 전자보다 애플리케이션에 특화된 사용자 정보를 이용해서 시뮬레이션 테스트를 할 수 있다.
결과 테스트가 정상적으로 실행되었다.
정리
- @WebMvcTest를 할 때, Spring Security의 설정 정보도 빈으로 가져오기 때문에 JwtAuthorizationFilter와 같이 SecurityConfig에서 의존하는 빈들이 없기 때문에 UnsatisfiedDependencyException이 발생한다.
- 개인적으로, 시큐리티의 필터는 컨트롤러 테스트가 아닌 직접 Postman을 통해 요청을 할 때 정상적으로 동작하는지 알 수 있기 때문에 컨트롤러 테스트에만 초점을 맞춰야한다고 생각했다.
- 그래서 Security 관련 설정을 스캔하지 않도록 @WebMvcTest의 excludeFilter에 Security 관련 설정을 추가해서 스캔 대상에서 제외했다.
- 단점으로 만약 Role을 여러개 다루고 있을 경우, Role마다 접근 가능한 경로가 달라질 수 있는데, 이에 대한 테스트가 불가능해진다는 단점이 있을 수 있겠다는 생각이 들었다.