I have the following project tree:
├── app
│ ├── build.gradle
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── child
│ │ │ └── app
│ │ │ └── Application.java
│ │ └── resources
│ │ └── application-default.yaml
│ └── test
│ └── java
│ └── child
│ └── app
│ └── ApplicationTest.java
├── build.gradle
├── childA
│ ├── build.gradle
│ └── src
│ └── main
│ └── java
│ └── child
│ └── a
│ ├── BaseGreeterImpl.java
│ ├── ChildAConfig.java
│ ├── Greeter.java
│ └── MySpringProperties.java
├── childB
│ ├── build.gradle
│ └── src
│ └── main
│ └── resources
│ ├── application-test.yaml
│ └── childB.properties
├── childC
│ ├── build.gradle
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── child
│ │ │ └── c
│ │ │ ├── ChildCConfig.java
│ │ │ └── PropertyGreeterImpl.java
│ │ └── resources
│ │ └── childc.properties
│ └── test
│ └── java
│ └── child
│ └── c
│ ├── TestYamlImport.java
│ └── TestGreeter.java
└── settings.gradle
I have the following test class :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ChildCConfig.class }, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
@SpringBootTest
public class TestYamlImport {
@Autowired
private MySpringProperties properties;
@Test
public void readChildAYaml() {
assertThat(properties.getName()).isEqualTo("it-is-another-thing");
}
}
I expect properties.getName()
to read the value from resource childB
in childB/src/main/resources/application-test.yaml.
I get null
GitHub: https://github.com/kopax/adk/tree/adk-spring
One liner:
git clone [email protected]:kopax/adk.git && cd adk && git checkout adk-spring && ./gradlew build --info
There is a test called childC/src/test/java/childC/TestGreeter.java in the reproduction project which prove with childB.properties
import that it is not a classpath issue.
So here are my questions :
Is spring limiting the classpath resolution somehow when using @ConfigurationProperties
?
I haven't found a way to read my application-test.yml
within a configuration @Bean
initialized in childA
from the test scope of childB
, how is this possible ?
Is there any particular reason you are using AnnotationConfigContextLoader
instead of (default) SpringBootContextLoader
? The problem you are facing is not caused by file missing in the classpath (you can copy application-test.yaml
to any src/main/resources
or src/test/resources
with the same result) but the fact that AnnotationConfigContextLoader
does not use ConfigFileApplicationListener
that is responsible for configuring context by loading properties from well known file locations (like application-{profile}.yaml
in your case).
You can easily compare what properties are loaded when using each loader. Firstly you can check what AnnotationConfigContextLoader
does - just put a breakpoint at line 128 of AbstractGenericContextLoader.java file and run debugger in your favorite IDE:
Next you can investigate variable context
-> environment
-> propertySources
-> propertySourceList
. You will find 5 property sources:
None of them loads properties from config files like application.yml
or application.properties
.
Now let's do the same but with SpringBootContextLoader
class. Firstly remove
loader = AnnotationConfigContextLoader.class
in MyEntityTest
and put a breakpoint at line 303 in SpringApplication.java file:
Here we are right before application context gets refreshed. Now let's investigate variable context
-> environment
-> propertySources
-> propertySourceList
:
The first difference we can see is that now we have 7 property sources instead of 5 as it was in the previous example. And what is most important - ConfigFileApplicationListener.ConfigurationPropertySources
is here. This class makes application context aware of application-{profile}.yaml
properties file existence.
So as you can see it is only a matter of using correct context loader. Replace
@ContextConfiguration(classes = { ChildCConfig.class }, loader = AnnotationConfigContextLoader.class)
with
@ContextConfiguration(classes = { ChildCConfig.class }, loader = SpringBootContextLoader.class)
or
@ContextConfiguration(classes = { ChildCConfig.class })
as this loader is a default one when using @SpringBootTest
annotation and you will make your test passing like a charm. I hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With