javaspringspring-bootspring-securityspring-security-rest

Spring WebMvcTest how to mock Authentication?


I have a controller with the following method. I get the Authentication from the method parameters and use it to get the principal.

@PatchMapping("/{employeeId}/present")
public PersonalDevelopmentPlan savePresent(@PathVariable int employeeId,
                                           @RequestBody PersonalDevelopmentPlan personalDevelopmentPlan,
                                           Authentication authentication) {
    EmployeeDetails details = (EmployeeDetails) authentication.getPrincipal();
    return pdpService.savePresent(employeeId, personalDevelopmentPlan, details.getEmployee().getId());
}

I then have this test case using @WebMvcTest

@Test
@WithMockUser
public void testSavePresent_returns_result_from_service() throws Exception {

    PersonalDevelopmentPlan pdp = new PersonalDevelopmentPlan();
    pdp.setEmployee(EMPLOYEE_ID);

    given(service.savePresent(eq(EMPLOYEE_ID), any(), anyInt())).willReturn(pdp);

    mvc.perform(patch(URL_WITH_ID + "/present").secure(true)
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(pdp)))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.employee", Matchers.is(EMPLOYEE_ID)));
}

When i run this though, i get a NullPointerException on the following line:

EmployeeDetails details = (EmployeeDetails) authentication.getPrincipal();

Because authentication is null. I already annotated the test with @MockUser but still authentication is null. How do i mock authentication in the parameters of the controller method?

My test is annotated with @AutoConfigureMockMvc(addFilters = false)

Thanks!


Solution

  • @WithMockUser This will use the principal on the Authentication is Spring Security’s User object


    @WithUserDetails

    While @WithMockUser is a very convenient way to get started, it may not work in all instances. For example, it is common for applications to expect that the Authentication principal be of a specific type. This is done so that the application can refer to the principal as the custom type and reduce coupling on Spring Security.

    The custom principal is often times returned by a custom UserDetailsService that returns an object that implements both UserDetails and the custom type. For situations like this, it is useful to create the test user using the custom UserDetailsService. That is exactly what @WithUserDetails does.


    @WithSecurityContext

    We can create our own annotation that uses the @WithSecurityContext to create any SecurityContext we want. For example, we might create an annotation named @WithMockCustomUser as @WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)