spring boot application context was not properly loaded if the yaml file wasn't application.yml - spring-boot

With following configuration, my test can read the properties from the yaml file correctly.
#SpringBootApplication
#PropertySource("classpath:application.yml")
#ComponentScan({ "com.my.service" })
public class MyApplication {
}
Then I renamed the yaml file to my-application.yml, and changed the PropertySource to
#PropertySource("classpath:my-application.yml")
Tests are failed due to the null property value. The configuration class is as following:
#Configuration
#ConfigurationProperties(prefix="my")
#Data
public class MyConfig {
private String attr1;
}
The test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
public class MyConfigTest {
#Autowired
private MyConfig myConfig;
#Test
public void getMyConfigTest() {
Assert.assertNotNull(myConfig.getAttr1());
}
Why spring boot can find the renamed yaml file, but it couldn't load the value correctly?

YAML files can’t be loaded via the #PropertySource annotation
It appears to work with #PropertySource("classpath:application.yml") because that's the default location and spring boot looks there regardless.
You may be able to use #ConfigurationProperties(location="claspath:my-application.yml") instead but it doesn't really achieve the same purpose (and I've never tried it myself).

Related

Getting properties in under test Service class by #Value returns null

I have a Spring Boot version 2.7.0 project with different profiles for dev and test with two different properties files: application-dev.properties and application-test.properties. (I have NO default application.properties file.)
In under test service class I have a property that I want to load its value from application-test.properties file. The service class:
#Service
#RequiredArgsConstructor
public class FileServiceImpl implements FileService {
#Value("${files.root-directory}")
private String fileDirectory;
#Override
#Transactional(readOnly = false)
public File createFile(CreateFileCommand command) {
var filePath = FileUtil.getPath(fileDirectory); // <- fileDirectory is null in tests
// ....
}
}
When I run the application in dev profile, everything is OK. But in tests, fileDirectory is always null.
Test class:
#SpringBootTest
#ActiveProfiles("test")
#ExtendWith(MockitoExtension.class)
public class FileServiceTest {
// ...
}
ApplicationTest class:
#SpringBootTest
#ActiveProfiles("test")
#TestPropertySource("classpath:application-test.properties")
#EnableConfigurationProperties
class ApiApplicationTests {
#Test
void contextLoads() {
}
}
application-dev.properties file:
files.root-directory=${user.home}\\api\\files
application-dev.properties file:
files.root-directory=/home/api/var/api/files
EDIT Screenshot of file structure
Put your properties file under src/test/resources for your (JUnit,Integration, Contract etc) test files. That should work!
Property files under src/main/resources are accessible to source files present under src/main/ only!

SpringBoot does not resolve #Value properties without PropertySource annotation

I have a bunch of #Value annotated fields in a SpringBoot configuration file, with the matching values in the standard application.properties . If I don't annotate the configuration file with #PropertySource("classpath:application.properties") it will just copy the "${prop1}" string into the actual variable.
I tried adding #EnableAutoConfiguration to the #Configuration class (instead of the PropertySource annotation), but all it does is to break when a requested property is not found.
SpringBoot is supposed to resolve the properties automatically from the standard application.properties file, why this behaviour? Using version 2.2.2.RELEASE
Update:
The answers are correct, the reason it was not working was that I was calling these properties in a test. Annotating the test with #SpringBootTest fixes the issue. In fact when the application is running it is #SpringBootApplication that does the magic
As you can read in this article (chapter 5), SpringBoot manage automatically the application.properties file.
I don't know if this is your problem because I've not seen the code, but in Spring Boot the Application class should be annotated with #SpringBootApplication.
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Take a look at this starting example.
You can then inject the value for example in a controller class in this way:
#RestController
public class HelloController {
#Value("${test}")
private String test;
#RequestMapping("/test")
String hello() {
return test;
}
}

application.properties not read with #EnableAutoConfiguration and custom spring boot starter

I try to create a simple custom spring boot starter that read property in application.properties :
#EnableConfigurationProperties({ CustomStarterProperties.class })
#Configuration
public class CustomStarterAutoConfiguration {
#Autowired
private CustomStarterProperties properties;
#Bean
public String customStarterMessage() {
return properties.getMessage();
}
}
with its ConfigurationProperties :
#ConfigurationProperties(prefix = "custom.starter")
public class CustomStarterProperties {
private String message;
/* getter and setter */
...
}
There is also the corresponding application.properties and META-INF/spring.factories to enable the autoconfiguration.
I have another project that declares this starter as a dependency and in which I write a test to see if the customStarterMessage Bean is created :
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
public class TotoTest {
#Autowired
String customStarterMessage;
#Test
public void loadContext() {
assertThat(customStarterMessage).isNotNull();
}
}
This test fails (even with the appropriate application.properties file in the project) because the application.properties seems to not be read.
It works well with a #SpringBootTest annotation instead of the #EnableAutoConfiguration but I would like to understand why EnableAutoConfiguration is not using my application.properties file whereas from my understanding all the Spring AutoConfiguration are based on properties.
Thanks
#EnableAutoConfiguration on test classes don't prepare required test context for you.
Whereas #SpringBootTest does default test context setup for you based on default specification like scanning from root package, loading from default resources. To load from custom packages which are not part of root package hierarchy, loading from custom resource directories you have define that even in test context configuration. All your configurations will be automatically done in your actual starter project based on #EnableAutoConfiguration you defined.

Spring Boot MockMVC Test does not load Yaml file

I have my configuration in application.yml file in the root of classpath (src/main/resources/). The configuration gets loaded fine when I start the application normally. However in my test the application.yml file gets not loaded at all.
The header of my test looks as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = Configuration.class)
#org.junit.Ignore
public class ApplicationIntegrationTest {
#Inject
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
...
The configuration class:
#EnableAutoConfiguration
#ComponentScan("c.e.t.s.web, c.e.t.s.service")
public class Configuration extends WebMvcConfigurerAdapter {
When I debug the application I see that the yml files get loaded in ConfigFileApplicationListener, in the test however the ConfigFileApplicationListener gets not called.
There is a whole chapter in the Spring Boot Reference guide regarding testing. This section explains how to do a basic test for a Spring Boot application.
In short when using Spring Boot and you want to do a test you need to use the # SpringApplicationConfiguration annotation instead of the #ContextConfiguration annotation. The #SpringApplicationConfiguration is a specialized #ContextConfiguration extension which registers/bootstraps some of the Spring Boot magic for test cases as well.
There is a good integration between StringBoot, jUnit and YAML.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MainBootApplication.class)
public class MyJUnitTests {
...
}
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "section1")
public class BeanWithPropertiesFromYML {
...
}
For more details please check my comment here: https://stackoverflow.com/a/37270778/3634283

Overriding Spring #PropertySource by #Import

I have a property test=default in class DefaultConfig, and I'm making them available using #PropertySource annotation.
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
I then want to be able to override to test=override, which is in a different properties file in class OverrideConfig, so I again use #PropertySource.
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {}
I configure a test to prove that it works.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={OverrideConfig.class})
public class TestPropertyOverride {
#Autowired
private Environment env;
#Test
public void propertyIsOverridden() {
assertEquals("override", env.getProperty("test"));
}
}
Except of course it does not.
org.junit.ComparisonFailure: expected:<[override]> but was:<[default]>
Maxing out debug, I can see what's happening:
StandardEnvironment:107 - Adding [class path resource [default.properties]] PropertySource with lowest search precedence
StandardEnvironment:107 - Adding [class path resource [override.properties]] PropertySource with lowest search precedence
It seems backwards. Am I making a simple mistake or misthinking this, or would you expect the properties defined by an #PropertySource in an #Import-ed configuration class to be overridden by properties defined in am #PropertySource in the #Import-ing class?
Here is a solution by Helder Sousa, written as a comment of the JIRA issue created by the OP:
[T]he behaviour available in spring xml (one xml importing another xml) is achievable using nested configurations:
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
#Configuration
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Configuration
#Import(DefaultConfig.class)
static class InnerConfiguration {}
}
With this setup, the properties will be gather in the proper order.
Today with Spring 4 you can use this:
#TestPropertySource(value="classpath:/config/test.properties")
And this can be used to use and eventually override properties for the junit test:
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(value="classpath:/config/test.properties")
I'm currently struggling with a similar case in Spring 3.1 but I'm using a different approach to override properties, because #PropertySource does not support optional property files:
#Configuration
#PropertySource("classpath:default.properties")
public class BaseConfig {
#Inject
private ApplicationContext context;
#PostConstruct
public void init() throws IOException {
Resource runtimeProps = context.getResource("classpath:override.properties");
if (runtimeProps.exists()) {
MutablePropertySources sources = ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources();
sources.addFirst(new ResourcePropertySource(runtimeProps));
}
}
...
It seems that #Import does not cause any specific order of #Configuration instantiation whatsoever besides the order dictated by normal bean dependencies. A way to force such an order is to inject the base #Configuration instance itself as a dependency. Could you try:
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Inject
private DefaultConfig defaultConfig;
...
}
Does this help?
And maybe the new ContextHierarchy annotation could be of help here also but I didn't try this one out so far.
You could enforce the loading order of your properties like this:
#Configuration
#PropertySource(value={"classpath:default.properties","classpath:override.properties"})
public class OverrideConfig {
...
}
I had a similar problem and succeeded in just declaring also the default property in my custom configuration:
#Configuration
#Import(DefaultConfig.class)
#PropertySource({"classpath:default.properties", "classpath:override.properties"})
public class OverrideConfig {}

Resources