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.ymlwithin 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