Spring boot tests - Can't find test properties - spring

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

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

How to make spring boot test app independent of external property source?

I am writing a controller test for a spring boot application. To use the spring application context I am using SpringRunner class. The problem is the main application class has a property source defined to a specific file path.
When I am running the test I am getting a FileNotFound exception from the hardcoded file. I want my test to be independent of this property source.
I cannot add the 'ignoreResourceNotFound' option for property source in the main application.
Below is the main application class with property source defined.
#SpringBootApplication
#PropertySource("file:/opt/system/conf/smto/management.properties")
#EnableConfigurationProperties
public class ManagementApp {
public static void main(String[] args) {
SpringApplication.run(ManagementApp.class, args);
}
}
I am also adding my test class below
#RunWith(SpringRunner.class)
#TestPropertySource(locations = {"classpath:application.properties","classpath:management.properties"})
#DirtiesContext
#EmbeddedKafka(topics = {"management-dev"},partitions = 1,
controlledShutdown = false,brokerProperties = {"listeners=PLAINTEXT://localhost:9092", "port=9092"})
#AutoConfigureMockMvc
#WebMvcTest(Controller.class)
public class ControllerTest {
}
I have found a workaround to create the spring context in this scenario. I have changed my testing class package and because of it, the spring-boot test cannot find the primary configuration class. And then provided all the required packages to create the application context.
Reference for this solution found from spring docs here.
Spring Boot’s #*Test annotations will search for your primary configuration automatically whenever you don’t explicitly define one.
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structured your code in a sensible way your main configuration is usually found.

Split jackson configuration into separate properties

I'm using Spring Boot 2.2.5.RELEASE and would like to split my application.properties into separate files. There are already similar questions on StackOverflow but none of them seem to work for configuring Jackson.
My current non working solution is the following:
root/
- application.properties (without Jackson configuration)
- jackson-configuration.properties (includes Jackson configuration)
Jackson configuration class:
#Configuration
#PropertySource("/jackson-configuration.properties")
public class JacksonConfiguration {
}
Please note, I've tried different ways to specify the path including:
"/jackson-configuration.properties"
"jackson-configuration.properties"
"classpath:/jackson-configuration.properties"
"classpath:jackson-configuration.properties"
Spring Boot does not seem to use the configuration. If I copy it over into the application.properties - it works.
Content of jackson-configuration.properties:
spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.mapper.sort-properties-alphabetically=true
spring.jackson.deserialization.fail-on-unknown-properties=true
spring.jackson.parser.strict-duplicate-detection=true
spring.jackson.time-zone=Europe/Zurich
My application is annotated with #SpringBootApplication , so it should scan for additional properties.
/edit
I just realized the problem is the testing, not the productive code itself. If I start the application it works. What doess not work is testing with #JsonTest. I can fix this problem by adding the following line to my tests #ContextConfiguration(classes = {JacksonConfiguration.class}). But in turn, this causes the annotation #JsonComponent to stop working but only for the #JsonTest annotated classes.
See the documentation here. Here is an excerpt from the documentation
In order to resolve ${...} placeholders in definitions or
#Value annotations using properties from a PropertySource, you must
ensure that an appropriate embedded value resolver is registered in
the BeanFactory used by the ApplicationContext. This happens
automatically when using in XML. When
using #Configuration classes this can be achieved by explicitly
registering a PropertySourcesPlaceholderConfigurer via a static #Bean
method.
You need to create a bean like this
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}

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 is not loading the properties from a YML file

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.

Resources