Spring Boot is not loading the properties from a YML file - spring

My test file is :
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class MyApplicationTest {
#InjectMocks
#Autowired
protected MyController myController;
}
My Controller file is:
#RestController
#RequestMapping(value="${app.base-path}") // Unable to load this property
public class MyController {
}
I have tried #TestPropertySource but did not work.
The SpringBootApplication works fine.
This is not the case with #Value annotation.
Fields decorated with #Value annotation works just fine.

Did you name your .yml file as application.yml and put it on the src/main/resources or src/test/resources?
Placing the application.yml file on those folders will automatically add it to the classpath so you can access this file properties at runtime.

By default, Spring Boot will try to load application.yml and application-{profilename}.yml available on classpath, so you could try with application-test.yml (it will eliminate the problem with defining profiles in one file - maybe it causes your problem).
Also, you could try with bootstrap.yml, which is loaded before application.yml.

Related

Spring Test : why cannot access application.properties?

I'm building an extremly easy Spring Boot application. It has no code, just a simple test :
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Value("${my.key}")
private String key;
#Test
public void test() {
System.out.println(key);
}
}
my.key is defined inside src/main/resources/application.properties (in main/ not test/)
The test doesn't pass because cannot find my.key property (but if I put this property inside src/test/resources/application.properties it works)
I'm sure I have seen plenty of Spring Boot applications where test classes read properties from /main/resources/application.properties
But here it does not work.
If you also add a application.properties file inside src/test/resources, this will "override" the one in src/main/resources, and hence none of the keys you define in your "production" properties file will be seen.
So either remove your application.properties in src/test/resources to use your property file in src/main/resources or define your values there.
You can use #TestPropertySource to override the location of your application.properties.
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource(locations="classpath:application.properties")
public class MyTest {
}
For further help on how to use it, please check here

Spring boot - test properties overriden with prod properties

My test properties are overriden with those placed in production properties.
At the very beggining I had both named application.yml but it didn't work, so I have changed like told in this post to application-test.yml and use profile.
Now it looks like bellow (kotlin):
#SpringBootTest
#ExtendWith(SpringExtension::class)
#ContextConfiguration(classes = [InvalidPropertiesApplication::class])
#ActiveProfiles("test")
#TestPropertySource(locations = ["classpath:application.yml"])
class InvalidPropertiesApplicationTests {
#Test
fun contextLoads(#Autowired users: Users) {
assertEquals("TEST", users.file)
}
}
in src/main/resources/application.yml I have only set this property
to PRODUCTION, in src/test/resources/application-test.yml to TEST.
And this test fails.
Full example can be found at github
Thx in advance.
Properties loaded with #TestPropertySource have a higher precedence than all other property sources in your example. And "classpath:application.yml" refers to src/main/resources/application.yml.
Order: #TestPropertySource > application-{profile}.properties/yaml > application.properties/yaml
See also the Spring Boot Reference Guide.

Enable configuration property in test without loading full Spring Boot application context

Assume we have Spring Boot application and want to load only specific slice of application context.
Particularly load YAML file configuration and map spring.datasource to DataSourceProperties marked by #ConfigurationProperties.
Naive not working declaration of test is:
#RunWith(SpringRunner.class)
#ContextConfiguration(
classes = {DataSourceAutoConfiguration.class, DataSourceProperties.class},
loader = AnnotationConfigContextLoader.class,
initializers = ConfigFileApplicationContextInitializer.class)
#TestPropertySource({"classpath:application.yaml", "classpath:application-dev.yaml"})
#EnableConfigurationProperties({DataSourceProperties.class})
#Slf4j
public class HibernateTest {
#Autowired
private DataSourceProperties dataSourceProperties;
#Test
public void dataSourceTest() throws SQLException {
log.info("DS URL: {}", dataSourceProperties.getUrl());
}
}
application-dev.yaml has:
spring.datasource:
url: jdbc:oracle:thin:#localhost:1521/APP
Test prints:
DS URL: null
I am looking for a way to map YAML config to #ConfigurationProperties marked class (DataSourceProperties) and make it used by #Configuration class (DataSourceAutoConfiguration) without loading any other services/components/etc...
Those links answer my questions:
https://jira.spring.io/browse/SPR-13912
Add support for YAML files to #PropertySource (Resolution: Unresolved).
https://jira.spring.io/browse/SPR-16563
Document that #PropertySource and #TestPropertySource do not support YAML.
https://github.com/spring-projects/spring-boot/issues/12388
Property Override not Working For YML Files.
https://github.com/spring-projects/spring-boot/issues/10772
Map properties in yaml from TestPropertySource not working in boot 2.0.0.
In short Spring Framework developers don't want to support YAML format for #TestPropertySource & #PropertySource annotation explaining that YAML support in Spring Boot made in tricky way / broken way (though I lost in explanations).
After switching code to use .properties files my test works fine.

Spring boot tests - Can't find test properties

I have a spring boot project and it works great. I now want to write tests for my application and I am running into some configuration headaches.
Spring boot created a test class for me called ApplicationTests. It's real simple and it looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class DuurzaamApplicationTests {
#Test
public void contextLoads() {
}
}
Now when I start the tests I get this error:
java.lang.IllegalArgumentException: Could not resolve placeholder 'company.upload' in value "${company.upload}"
I have a properties.yml file in the src/test/resources directory and for some reason it isn't loaded. I have tried all different kind of annotations from examples on the Internet and yet none of them work.
How can I tell spring boot tests to use an application.yml file to load the properties from?
We can use #TestPropertySource or #PropertySource to load the properties file.
Example:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource("classpath:properties.yml")
#ActiveProfiles("test")
public class DuurzaamApplicationTests {
#Test
public void contextLoads() {
}
}
Docs: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html
To my surprise, when you load properties files in Spring Boot Test, .yml is not supported. It's noted in the documentation, although implicitly.
From the link above:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html
Supported File Formats
Both traditional and XML-based properties file formats are supported — for example, "classpath:/com/example/test.properties" or "file:/path/to/file.xml".
.yml is not mentioned.
And, after changing my .yml to .properties and rewrite the values in xx.xx.xx=value form, the key-values pairs can be read correctly.
So strange.
EDIT:
Now I find a ticket address this issue; seems a long-known bug in Spring.
https://github.com/spring-projects/spring-framework/issues/18486
#PropertySource and #TestPropertySource do not work with YAML. See this.
I also tested it myself. Try creating 2 files - *.yml and *.properties and see it for yourself.
To make *.yml work most people use #SpringBootTest, but if it's not what you want and you would like to use #ContextConfiguration instead, you are in for a bit of surprise.
For me the above solutions did not work and any environment variables were still overriding the test properties defined in #TestPropertySource even though https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html indicates that this source should have higher precedence than environment variables. The only solution that worked for me was to manually define a PropertyPlaceholderConfigurer bean in a test configuration class and set it with highest precedence.
This was with Spring Boot 1.5.15.RELEASE
#Configuration
#TestPropertySource(properties = "/application-test.properties")
#Slf4j
public class IntegrationTestConfiguration {
#Bean
public static PropertyPlaceholderConfigurer properties() {
PropertyPlaceholderConfigurer ppc
= new PropertyPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[]
{ new ClassPathResource( "/application-test.properties" ) };
ppc.setLocations( resources );
ppc.setIgnoreUnresolvablePlaceholders( true );
ppc.setOrder( Ordered.HIGHEST_PRECEDENCE );
return ppc;
}
/// ....
#RunWith( SpringRunner.class )
#ActiveProfiles( "test" )
#Import( IntegrationTestConfiguration.class )
#SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
public class MyTest {
I had the same error message, my problem was a application.properties in src\test\resources which was missing the new properties
Sometimes your application-test.properties file can't be found because it is in a subfolder off the class path.
for example this may not be found, because the file is actually not directly in the class path.
#TestPropertySource("classpath:application-test.properties")
but this will be found if the file is in the config folder off of a path in the class path
#TestPropertySource("classpath:config/application-test.properties")
We can use annotation of #ActiveProfiles("test") that support application-test.yml or application-test.properties

How to dynamically register the bean specific properties on bean initialization

I want to register the bean specific properties on bean creation if not exists in the application. For example
public class BeanOne {
#Value("${prop1}")
private String property1;
}
public class BeanTwo {
#Value("${prop2}")
private String property2;
}
I want to register the property 'prop1' and 'prop2' to instantiate the above two beans successfully.
My application is configured with spring-cloud-consul-config. By default consul is not configured with these properties in that time, application will automatically register these properties into consul through ecwid/consul-api
Currently the application throws "IllegalArgumentException" with message "Could not resolve placeholder 'prop1' in string value "${prop1}"
Any Suggestions to avoid the above error.
There are various ways you can do this.
My preferred approach is to set the properties in one or more files externally and then use the #PropertySource("file:///full-path-to-property-file") annotation in a #Configuration class.
This approach, combined with Spring Profiles is very powerful.
Example if you are not using profiles:
Create an application.properties file somewhere on your PC. A good
place to put this is ${user.home}/.somefolder/application.properties
Add the values for prop1 and prop2
Change the permissions on that folder and file. For example you can
run chmod u on .somefolder and chmod 600 on the properties file
Create a Spring Java Configuration class and annotate it with
#Configuration. For example you can create an ApplicationConfig
class in a sub package of your Spring Boot application and annotate
it with #Configuration
Add the #PropertySource annotations as follows:
#PropertySource("file:///${user.home}/.somefolder/application.properties")
An example from my course on Spring Boot, AWS, Bootstrap and Stripe:
#Configuration
#EnableJpaRepositories(basePackages = "com.devopsbuddy.backend.persistence.repositories")
#EntityScan(basePackages = "com.devopsbuddy.backend.persistence.domain.backend")
#EnableTransactionManagement
#PropertySource("file:///${user.home}/.devopsbuddy/application-common.properties")
public class ApplicationConfig {
}
That's it!

Resources