- Notifications
You must be signed in to change notification settings - Fork 41.6k
Description
Given the following configurations:
package com.example.demo.a; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.example.outside_app.SomeService; @Configuration public class SomeConfig { @Bean public SomeService someServiceFromA() { return new SomeService(); } }
and in another package, under the same, simple name:
package com.example.demo.b; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.example.outside_app.SomeService; @Configuration public class SomeConfig { @Bean public SomeService someServiceFromB() { return new SomeService(); } }
Spring Framework refuses to work with two configuration classes having the same bean name (someConfig
). This is fine, the error is usable.
However, the ApplicationContextRunner
does not do this and takes in the following configuration
@Test public void f2() { new ApplicationContextRunner() .withUserConfiguration(com.example.demo.a.SomeConfig.class, com.example.demo.b.SomeConfig.class) .run(ctx -> { assertThat(ctx).hasBean("someServiceFromB"); assertThat(ctx).hasBean("someServiceFromA"); }); }
And fails the test by silently ignoring or overwriting the first configuration given.
While it would have been noticed in this case, you don't notice it when you test for the absence of one bean and the presence of another. As one config has maybe silently dropped, conditions are not tested.
This happens also with overlapping autoconfigurations having the same class name and this is where I actually found the issue:
Spring Boot and or Spring Framework do apparently support autoconfigurations with the same simple class name.
Given this bean
package com.example.outside_app; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JpaRepositoriesAutoConfiguration { @Bean public SomeService someServiceFromAutoConfig() { return new SomeService(); } }
and Spring Factories containing
org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.example.outside_app.JpaRepositoriesAutoConfiguration
I can test my application:
@RunWith(SpringRunner.class) @SpringBootTest public class DemoApplicationTests { @Autowired private Map<String, SomeService> someServices; @Test public void contextLoads() { assertThat(someServices).containsKeys("someServiceFromAutoConfig"); } }
However, the ApplicationContextRunner
disagrees and
@Test public void onlyAutoConfig() { new ApplicationContextRunner() .withConfiguration( AutoConfigurations.of( org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.class, com.example.outside_app.JpaRepositoriesAutoConfiguration.class )) .run(ctx -> assertThat(ctx).hasBean("someServiceFromAutoConfig")); }
fails.
While the duplicate names need good reasons and are of course a thing to debate, the application context runner should agree during tests with the framework to prevent errors in custom starters or applications, depending on what is actually tested.
I have attached the demo project.