5

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.

  1. In the test class, why the @TestConfiguration can not detect @TestComponent located in the same test, I have to add @Import to fix it.
  2. 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 a WebSecurityConfigurerAdapter and expose AuthenticationManager for test, why? And more confused me is as described before , running the tests one by one is ok if there is no WebSecurityConfigurerAdapter defined for test.
  3. The @TestConfiguration annotated WebSecurityConfigurerAdapter 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?
Hantsy
  • 8,006
  • 7
  • 64
  • 109
  • It should work if you put the Config class outside of your test class. Any reason you need it all in the one class? – Dovmo Jan 28 '18 at 07:07
  • @Dovmo Tried to move out of test class, got the same result. – Hantsy Jan 28 '18 at 13:52
  • @Dovmo This may be a specific issue when loading application context of the SpringSecuirty, in [another test](https://github.com/hantsy/spring-microservice-sample/blob/master/post-service/src/test/java/com/hantsylabs/sample/springmicroservice/post/PostServiceTest.java), `TestConfiguration` works. – Hantsy Jan 28 '18 at 13:56
  • 2
    Had a problem when `@TestConfiguration` applied to late. By that time another component with `@ConditionalOnBean` had already been skipped. Using `@Order(Ordered.HIGHEST_PRECEDENCE)` helped. – leonid Sep 08 '20 at 13:04
  • @leonid `@Order(Ordered.HIGHEST_PRECEDENCE)` worked perfectly for my issue of `@TestConfiguration` processing after my `@ConditionalOnBean`. – GreenSaguaro Mar 12 '21 at 23:46

1 Answers1

0

Simply adding @Order(Ordered.HIGHEST_PRECEDENCE) solved it in my case:

@Order(Ordered.HIGHEST_PRECEDENCE)
@TestConfiguration

I'm not quite sure why that isn't the default. My @ConditionalOnBean was evaluated before that @TestConfiguration was actually initializing those beans.

payne
  • 4,691
  • 8
  • 37
  • 85