Hi,
We have a homemade custom starter. I have made a minimal example with a class containing a ConfigurationProperties with a prefix and a @AutoConfiguration class with a ConditionalOnProperty
From Spring Boot 3.4.0, our tests fail, because it seems that now, the ApplicationContextRunner is aware of 2 differents beans for the ConfigurationProperties. One with, and one without prefix.
The test looks like this
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
IamApplicationSecurityConfig.class,
IamConfigurationProperties.class
));
@Test
void testCreatesIamApplicationSecurityConfigBeanOnPropertyDetection() {
contextRunner
.withPropertyValues(IAM_SECURITY_AUTOCONFIGURE_PROPERTY + ":true")
.run(context -> assertThat(context).hasSingleBean(IamApplicationSecurityConfig.class));
}
If we switch to
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(
IamApplicationSecurityConfig.class,
IamConfigurationProperties.class
);
```
tests work again with Spring Boot `3.4.0`. Seems related to this change. https://github.com/spring-projects/spring-boot/issues/17963
Not sure if this is expected or not.
I have made a minimal repository that reproduces this behavior : https://github.com/mpalourdio/ApplicationContextRunnerRegression
As is, tests are ok. Switch to `3.4.0` [here](https://github.com/mpalourdio/ApplicationContextRunnerRegression/blob/master/dependencies/pom.xml#L17), tests fail with
```log
Expecting:
<Unstarted application context org.springframework.boot.test.context.assertj.AssertableApplicationContext[startupFailure=org.springframework.beans.factory.UnsatisfiedDependencyException]>
to have a single bean of type:
<com.mpalourdio.springbootstarter.IamApplicationSecurityConfig>:
but context failed to start:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.mpalourdio.springbootstarter.IamApplicationSecurityConfig': Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.mpalourdio.springbootstarter.IamConfigurationProperties' available: expected single matching bean but found 2: com.mpalourdio.springbootstarter.IamConfigurationProperties,iam-com.mpalourdio.springbootstarter.IamConfigurationProperties
Comment From: wilkinsona
Thanks for the sample.
This kind of subtle change in behavior is why we only fixed #17963 in 3.4.0 rather than in a 3.3.x or even 3.2.x maintenance release. I think the behavior is now correct and this an example of the inconsistent behavior that we've fixed.
IamApplicationSecurityConfig has @EnableConfigurationProperties(IamConfigurationProperties.class). Due to which the configuration of the runner declaring IamApplicationSecurityConfig has an auto-configuration class, this will result in an IamConfigurationProperties bean being defined (with the prefix). You're also declaring IamConfigurationProperties itself as an auto-configuration which is resulting in it being defined again (without the prefix).
IamConfigurationProperties is neither an auto-configuration class nor a user configuration class so it shouldn't be declared at all:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
IamApplicationSecurityConfig.class
));
The test passes with this configuration with both Spring Boot 3.3.6 and 3.4.0. It's also arguably a better test as it's now verifying that IamApplicationSecurityConfig is enabling the configuration properties class that it needs.