Summary

Hello Spring. Forgive me if this is better transferred to Spring Framework, but I found an inconsistent behavior with AutoConfiguration ordering in a @SpringBootTest when using @ImportAutoConfiguration so posting it here first.

Consider the case where I have 3 AutoConfigurations where one of the AutoConfigurations wants to run before one, but after another, and one of these AutoConfiguration comes from another spring library.

The library AutoConfiguration

@AutoConfiguration
public class BarAutoConfiguration {

    @Bean
    public Bar bar() {
        return new Bar();
    }
}

And the other two AutoConfiguration

@Configuration
@AutoConfigureBefore(AppConfig.class)
@AutoConfigureAfter(name = "com.example.library.BarAutoConfiguration")
@ConditionalOnClass(Bar.class)
@ConditionalOnBean(Bar.class)
public class FooConfig {

    @Bean
    public Foo foo(Bar bar) {
        return new Foo(bar);
    }

}

@Configuration
public class AppConfig {

    @Bean
    public ConsumerOfFoo consumerOfFoo(List<Foo> foo) {
        return new ConsumerOfFoo(foo);
    }
}

I then want to have a a tests that Loads all 3 of these in this manner.

@SpringBootTest(classes = {AppConfig.class, FooConfig.class}, properties = {"debug=true"})
@ImportAutoConfiguration(BarAutoConfiguration.class)
public class FooConsumerTest {

    @MockitoSpyBean
    private Bar bar;

    @Autowired
    ConsumerOfFoo consumerOfFoo;

    @Test
    public void spanishTest() {
        Mockito.when(bar.getGreeting()).thenReturn("Hola");
         // test assertions
    }
}

In this case, BarAutoConfiguration is not ran before FooConfig as directed by @AutoConfigureAfter(name = "com.example.library.BarAutoConfiguration") so the conditions of the test breaks as the Foo bean is not created early enough. The specific ordering directives do not seem to be applied as expected.

Expectations

AutoConfiguration ordering would be preserved in @SpringBootTest for imported AutoConfigurations.

Workarounds

I can, of course, move BarAutoConfiguration into classes {} block and config order behaves as expected. The reason I might not want to do this is I may want to eventually create a test slice for BarAutoConfiguration and apply that annotation in this tests so consumers of the library don't need to know the specific configuration to add. This is a pattern we've been adding to most of our libraries to allow users to create smaller and more efficient tests without understanding the code structure and using documented test slices instead.

Reproducer

I dropped a Reproducer of this example here. You can run this test to reproduce the ordering problem.

Comment From: snicoll

What is the purpose of @ImportAutoConfiguration(BarAutoConfiguration.class) on that test?

Comment From: iparadiso

What is the purpose of @ImportAutoConfiguration(BarAutoConfiguration.class) on that test?

To also import it into the test as it's needed as an external auto-configuration for the minimal test.
It's really a placeholder of an custom tests slice as I mention in the workarounds section. The goal is minimal tests that don't load the world and write custom test slice annotations so users don't need to understand the library code structure to bring those beans into a tests.