When @ConfigurationProperties(prefix="..") is used with @Bean on an @Configuration class, it doesn't populate the fields of the bean created when the configuration is loaded through @SpringBootTest classes property. But does work when the configuration is loaded through the component scanning that @SpringBootApplication does.
My use case is actually more complex, which makes using an @SpringBootApplication much less desirable, but the following simple example illustrates what I'm seeing.
As a baseline, the following works:
@Configuration
public class Config {
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return new DataSource();
}
}
@Data
public class DataSource {
private String url;
}
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
application.properties:
spring.datasource.url = abc
@SpringBootTest(properties = {"spring.datasource.url=xyz"})
class DataSourceLoadingTests {
@Autowired
DataSource dataSource;
@Test
void testDataSource() {
assertThat(dataSource).isNotNull();
assertThat(dataSource.getUrl()).isEqualTo("xyz");
}
}
But if I remove the SpringBootApp class above and instead load the config through the @SpringBootTest annotations' classes property the following test:
@SpringBootTest(
properties = {"spring.datasource.url=xyz"},
classes = {Config.class}
)
class DataSourceLoadingTests {
@Autowired
DataSource dataSource;
@Test
void testDataSource() {
assertThat(dataSource).isNotNull();
assertThat(dataSource.getUrl()).isEqualTo("xyz");
}
}
fails as follows:
org.opentest4j.AssertionFailedError:
Expecting:
<null>
to be equal to:
<"xyz">
but was not.
Expected :xyz
Actual :null
The first assertion passes, because the DataSource instance was not null, which I believe shows the Config class's @Bean creates the instance.
But the second assertion fails because the url field is not populated, which I believe shows that the @ConfigurationProperties(prefix="spring.datasource") did not result in the url field being populated.
Comment From: spencergibb
Config.class needs @EnableAutoConfiguration
Comment From: snicoll
@pluttrell the auto-configuration is not a mandatory feature of Spring Boot. That @SpringBootTest with a single @Configuration class is just going to load that. And there is no infrastructure in place to bind your @ConfigurationProperties object.
The behaviour you've described is the expected one. For follow-up and questions, please use StackOverflow or Gitter, as mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.
Comment From: PMacho
@snicoll What do you mean by "there is no infrastructure in place"? I mean, isn't @SpringBootTest supposed to start a "normal" Spring Boot application? As we see, it does load the context and also instantiates the DataSource bean, which is pretty much the expected behavior. It just does not populate the values, which from a user perspective definitely is not expected, since a "normal" Spring Boot application would. By "infrastructure" do you mean there is no means of parsing the values around, or what are you referring to? I mean, the reflection capabilities of the JVM to bind properties to the proxy definitely are around. Imao this is a legit bug report since the behavior of the test alters from the behavior of the "normal" ApplicationContext and there is not even any warning around, that would inform the user about missing property binding capabilities. Maybe from a Spring core developers perspective this looks different but I was supposing the framework would not require every user to entirely understand every implementation detail only to use basic configuration management.
Comment From: PMacho
... by the way, property binding works flawless when using @SpringBootTest without the classes option. Yes, we can now guess that in this case the full "infrastructure" is around. But honestly, when testing we of course only want to configure what's required for the test and not just always the entire ApplicationContext.
Comment From: PMacho
@spencergibb Your suggestion does fix the issue. Could you elaborate on this? It typically is not required, only when using @SpringBootTest(classes = {...]).
Comment From: wilkinsona
@PMacho As Stephane said in his comment to which you're responding, we prefer that questions are asked on Stack Overflow. The issue tracker isn't the place for this
Comment From: PMacho
@wilkinsona I think so too. But this is not a question. The framework does not behave as expected from a user perspective, which in my opinion is a bug.
Comment From: wilkinsona
Please read this section of the documentation. It explains the behaviour you're seeing with @Configuration which disables the search for a class annotated with @SpringBootConfiguration or @SpringBootApplication. This very likely means that auto-configuration has not be enabled and, therefore, configuration property binding hasn't been auto-configured.
If you have any further questions, please ask on Stack Overflow.
Comment From: PMacho
@wilkinsona Auto-configuration is not required for the Config.class to be configured. It is configured explicitly. The article doesn't say anything about property binding being disabled when auto-configuration is switched off. Type-safe configuration properties is one of the fundamental Spring Boot features. The documentation mentions that "Sometimes, classes annotated with @ConfigurationProperties might not be suitable for scanning" (whatever that means). But this is not the problem, scanning did work. Property binding however didn't.
I still don't have any user space questions. That is why I am not writing on StackOverflow. I only want to support the fact that this is a bug. It is a bug because it is logically inconsistent with the rest of the frameworks' behavior. Of course, the origin might be clear to framework developers but it is very surprising from a user perspective.