Piotr Olaszewski
Piotr Olaszewski

Reputation: 6204

How to use spring security in integration tests?

I have a following WebSecurityConfigurerAdapter implementation:

@Configuration
@Order(0)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private static final List<String> permittedPaths = asList();
    private static final String protectedPath = "";

    private final UserDetailsServiceImpl userDetailsService;
    private final JwtAuthenticationProvider jwtAuthenticationProvider;
    private final DataSource dataSource;

    public SecurityConfiguration(
        UserDetailsServiceImpl userDetailsService,
        JwtAuthenticationProvider jwtAuthenticationProvider,
        DataSource dataSource
    ) {
        this.userDetailsService = userDetailsService;
        this.jwtAuthenticationProvider = jwtAuthenticationProvider;
        this.dataSource = dataSource;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {}

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        auth.authenticationProvider(jwtAuthenticationProvider);
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("")
                .authoritiesByUsernameQuery("")
                .passwordEncoder(passwordEncoder());
    }
}

This security works fine for normal running application. But in tests - fails. I have an integration test like:

@WebMvcTest(SomeController.class)
@Import({ErrorHandlerConfiguration.class})
class SomeControllerItTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private RegistrationService registrationService;

    @Test
    void shouldConfirmRegistration() {}
}

and after run, I get following error:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfiguration' defined in file [SecurityConfiguration.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'UserDetailsServiceImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

When I add to this test SecurityConfiguration class beans:

@MockBean
private JwtAuthenticationProvider jwtAuthenticationProvider;
@MockBean
private UserDetailsServiceImpl userDetailsService;
@MockBean
private DataSource dataSource;

tests runs normally, without any NoSuchBeanDefinitionException exceptions.

This solution isn't enough for me. Is there any other way to avoid putting security beans to each integrtation tests?

I tried to use:

without any results.

(removed methods body and some strings for brevity)

// EDIT Attached missing classes, which are injected into SecurityConfiguration:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
//method implementation
}

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
//method implementation
}

Upvotes: 2

Views: 1635

Answers (2)

Roar S.
Roar S.

Reputation: 10679

@WebMvcTest(
    value = SomeController.class,
    excludeAutoConfiguration = SecurityAutoConfiguration.class,
    excludeFilters = @ComponentScan.Filter(
            type = FilterType.ASSIGNABLE_TYPE,
            classes = WebSecurityConfigurer.class))
@AutoConfigureMockMvc(addFilters = false)
public class SomeControllerTest {

    @Autowired
    MockMvc mockMvc;
    
    ...
}

Upvotes: 2

Toerktumlare
Toerktumlare

Reputation: 14712

this is a qualified guess, but for some reason you have not posted the class that is missing UserDetailsServiceImpl.

If you read the documentation about WebMvcTest it states:

@WebMvcTest auto-configures the Spring MVC infrastructure and limits scanned beans to @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, and HandlerMethodArgumentResolver.

Regular @Component and @ConfigurationProperties beans are not scanned when the @WebMvcTest annotation is used. @EnableConfigurationProperties can be used to include @ConfigurationProperties beans.

So im guessing your bean isn't picked up for this reason, its all stated in the docs.

But as said, this is a guess because for some reason you have not posted the code that is missing for us to take a look at, and there are several unknows, as to the empty constructor, and other empty functions.

Upvotes: 0

Related Questions