Mock Custom User
Building a Mock object for custom UserDetails implementations in Spring Security
While working through the Spring Security aspects of my SpringBikeClinic.com project, I implemented SecurityUser, a custom UserDetails object which was composed with a User database entity. For unit testing my @Controller code, I wanted a clean way to provide a mock instance of my custom user type.
There are a few annotations provided by Spring Security that allow us to provide a user when tests are run:
If we are simply using the default User object from Spring Security, the @WithMockUser is the easy choice. This option allows us to customize the username as well as the roles or authorities used for the test method or class. Another option, @WithUserDetails, is flexible but requires your user to exist and be accessible from a UserDetailsService instance.
It can also be useful to test using the @WithAnonymousUser annotation, either for areas that are intended to be used by unauthenticated users, or to verify than anonymous users are blocked from functionality that requires authentication. For example:
@Test
void getAccountDetails_asAnonymousUser_isForbidden() throws Exception {
mockMvc.perform(get("/your/path/here"))
.andExpect(status().isUnauthorized());
verifyNoInteractions(someService);
}
However, when your tests require a specific, custom Authentication type, we can define our own annotation:
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
String username() default "user";
String password() default "pwd";
String firstName() default "Firstname";
String lastName() default "Lastname";
}
By applying @WithSecurityContext to this @WithMockCustomUser annotation, Spring Security will use the specified WithSecurityContextFactory to instantiate a SecurityContext.
public class WithMockCustomUserUserSecurityContextFactory
implements WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(WithMockCustomUser annotation) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authority authorityCustomer = Authority.builder().role("CUSTOMER").build();
String password = "{noop}password";
User principal = User.builder()
.id(1L)
.email("user@yourdomain.com")
.password(password)
.firstName("Firsty")
.lastName("McLasty")
.authority(authorityCustomer)
.build();
SecurityUser securityUser = new SecurityUser(principal);
Authentication auth = new UsernamePasswordAuthenticationToken(
securityUser, password, securityUser.getAuthorities() );
context.setAuthentication(auth);
return context;
}
}
With these annotations defined, we can simply annotate our test methods with @WithMockCustomUser and know that we will have a valid instance of our custom user type provided by Spring Security. For example:
@WithMockCustomUser
@Test
void getAccountDetails_asAuthenticatedUser_isOk() throws Exception {
mockMvc.perform(get("/your/path/here"))
.andExpect(status().isOk())
.andExpect(view().name("yourViewName"));
}