2

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:

  • @Import({ErrorHandlerConfiguration.class, SecurityConfiguration.class})
  • @ContextConfiguration(classes = {SecurityConfiguration.class})

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
}
Piotr Olaszewski
  • 6,017
  • 5
  • 38
  • 65
  • Maybe this will be to any help: https://stackoverflow.com/a/63508842/14072498 – Roar S. Feb 14 '21 at 21:26
  • Thanks for the link. But main difference is that `@SpringBootTest` runs whole ApplicationContext (i.e. with persistence layer) which is not needed for me to integration test which testing a controller responses. Maybe there isn't any other option and I must run whole context. Anyway thans for tip. – Piotr Olaszewski Feb 15 '21 at 05:23
  • 2
    why is your constructor in `SecurityConfiguration` empty? – Toerktumlare Feb 15 '21 at 08:22
  • @Toerktumlare constructor is empty for brevity. Parameters are assigned to class fields. I fixed it. – Piotr Olaszewski Feb 16 '21 at 06:30

2 Answers2

2
@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;
    
    ...
}
Roar S.
  • 8,103
  • 1
  • 15
  • 37
0

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.

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54