Spring Boot | Spring Security Test @WithMockUser를 커스터마이징 해서 사용하자
스프링 시큐리티가 적용되어 있는 프로젝트에서 컨트롤러 통합 테스트 작성 중..
@WithMockUser
만으로 해결되지 않는 경우가 발생했다.
@WithMockUser
가장 쉽게 특정 유저를 설정해 테스트를 진행하는 방법이다. username
, password
, roles
등 항목을 입력할 수 있다.
아래와 같이 사용하면, test라는 유저명을 가진 가짜 유저를 생성한다(mocking user)
물론 입력한 값들이 사용된 Authentication
이 SecurityContext
에도 올라간다.
@WithMockUser(username = "test", roles = "USER")
@Test
void withMockUserTest() { ... }
하지만 커스텀된 Authentication 인증 정보는 사용할 수 없다.@WithUserDetails
라는 애노테이션도 있지만 UserDetailsService
를 사용해 Authentication을 만드는 경우에만 해당된다.
UserDetailsService
는 오직loadUserByUsername()
메서드만 갖고 있고,UserDetails
형식만 반환 가능하다
좀 더 유연하게 원하는 Authentication을 사용하고자 한다면 @WithSecurityContext
를 사용해야 한다.
@WithSecurityContext
우선 애노테이션을 만들어준다.
애노테이션 생성 시 @WithSecurityContext
애노테이션을 붙여주어야 한다.@WithSecurityContext
애노테이션은 스프링 시큐리티 테스트용 SecurityContext
를 만들겠다는 사인이라고 생각하면 된다. factory
라는 값을 필수로 입력해야 하는데, 해당 클래스에서 우리가 만든 @WithMockCustomUser
애노테이션에게 새로운 SecurityContext를 생성해 전달해야 한다.
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
String username() default "foo";
String grade() default "ADMIN";
}
위에 @WithSecurityContext
애노테이션에 전달한 WithMockCustomUserSecurityContextFactory
클래스를 만든다.
이 클래스는 WithMockCustomUserSecurityContextFactory
인터페이스를 구현하여 작성한다.
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(WithMockCustomUser annotation) {
final SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
final UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(annotation.username(),
"password",
Arrays.asList(new SimpleGrantedAuthority(annotation.grade())));
securityContext.setAuthentication(authenticationToken);
return securityContext;
}
}
이제 @WithMockCustomUser
애노테이션을 사용할 수 있다.
default 값이 모두 있으므로 아무 값을 입력하지 않고도 사용할 수 있다.
@WithMockCustomUser
@Test
void getUserList() throws Exception {
ResultActions perform = this.mvc.perform(get("/users")
.param("page", "0")
.param("size", "20"));
//...생략
}