According to Spring Boot Docs, a nested @TestConfiguration
should be detected by tests automatically.
But in my test codes it is problematic when I ran the whole test class, it was not detected even I added it explicitly by @Import
. The test code structure is like the following:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
//@Import(IntegrationTests.TestSecurityConfig.class)
public class IntegrationTests {
// test methods
// test configuration
@TestConfiguration
static class TestSecurityConfig {}
}
When I ran single test cases(test methods) individually, all tests are passed as expected, but when I ran the test class directly, some tests are failed, the @TestConfiguration
was not applied to test.
The complete codes of this IntegrationTests
is here.
UPDATE: A workaround added in my codes to make the tests passed.
@TestComponent
@Slf4j
static class TestUserDetailsService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
TestUserDetailsService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = User.withUsername("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false)
.build();
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder.encode("password"))
.roles("ADMIN")
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false)
.build();
log.debug("dummy user:" + user);
log.debug("dummy admin:" + admin);
if ("user".equals(username)) {
return user;
} else {
return admin;
}
}
}
@TestConfiguration
@Slf4j
@Import(TestUserDetailsService.class)
@Order(-1)
static class TestSecurityConfig extends WebSecurityConfigurerAdapter {
@Inject
PasswordEncoder passwordEncoder;
@Inject
UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/posts/**").permitAll()
.antMatchers(HttpMethod.DELETE, "/posts/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
}
There are still some things confused me.
- In the test class, why the
@TestConfiguration
can not detect@TestComponent
located in the same test, I have to add@Import
to fix it. - As described in the security section of Spring Boot Docs, I was thinking defining a
UserDetailsService
bean is enough, it will serve the users in security, but it did not work in tests. I have to configure aWebSecurityConfigurerAdapter
and exposeAuthenticationManager
for test, why? And more confused me is as described before , running the tests one by one is ok if there is noWebSecurityConfigurerAdapter
defined for test. - The
@TestConfiguration
annotatedWebSecurityConfigurerAdapter
does not get a higer order, I have to add@Order
on it. I was thinking a@TestConfiguration
bean should get Primary automatically and replace the bean in my application config, right?