mardi 30 juillet 2019

Mock WebSecurity dependencies when testing controllers in Spring

To have fast tests for my controller I want to use @WebMvcTest. I wrote server-side unit tests for the controller (See DemoControllerTests in the demo project). These are basically simple unit tests involving the sliced Spring application context:

@WebMvcTest(DemoController.class)
class DemoControllerTests {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithAnonymousUser
    void shouldNotHaveAccessWhenAnonymous() throws Exception {
        this.mockMvc.perform(get("/"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(username = "pascal", roles = "USER")
    void shouldHaveAccessWithUserRole() throws Exception {
        this.mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string("Hello"));
    }
}

But as soon as is start involving more dependencies in my WebSecurityConfig like the MyUserDetailsService (which itself has a dependency on the UserObjectRepository) the problems start to appear.

@Autowired
public WebSecurityConfig(final MyUserDetailsService myUserDetailsService) {
    this.myUserDetailsService = myUserDetailsService;
}

When I run the tests Spring cannot load the ApplicationContext (NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.MyUserDetailsService' available).

This does make sense when I take a look at the documentation of @WebMvcTest. It says that components, services, and repositories will not be auto-configured. But in the last section, it says that Spring Security will be auto-configured when using this annotation. Additional configuration is available using the @AutoConfigureMockMvc.

From my point of view there nothing special to do and even though I want to set the secure config in @AutoConfigureMockMvc it is enabled by default and deprecated. They mention that the secure property is deprecated since 2.1.0 in favor of Spring Security's testing support. But I cannot find more details about a dedicated Spring Security testing support regarding this topic (i already use @WithMockUser etc. in my tests)

 * <p>
 * Using this annotation will disable full auto-configuration and instead apply only
 * configuration relevant to MVC tests (i.e. {@code @Controller},
 * {@code @ControllerAdvice}, {@code @JsonComponent},
 * {@code Converter}/{@code GenericConverter}, {@code Filter}, {@code WebMvcConfigurer}
 * and {@code HandlerMethodArgumentResolver} beans but not {@code @Component},
 * {@code @Service} or {@code @Repository} beans).
 * <p>
 * By default, tests annotated with {@code @WebMvcTest} will also auto-configure Spring
 * Security and {@link MockMvc} (include support for HtmlUnit WebClient and Selenium
 * WebDriver). For more fine-grained control of MockMVC the
 * {@link AutoConfigureMockMvc @AutoConfigureMockMvc} annotation can be used.
 * <p>

The tests are fine in general. They test the controller methods and their method security configuration. The problems only start to roll in when I add more dependencies and separate classes which I then try to inject in my WebSecurityConfig.

How can I mock these dependencies so that my controller tests are working with a sliced context instead of starting the whole application context with @SpringBootTest?

Here is the demo project that shows the problem: https://github.com/pas2al/spring-playground

Aucun commentaire:

Enregistrer un commentaire