How to use #Value in Junit tests - spring-boot

I'm trying to get a property value from my application-test.yml file like that :
#SpringBootTest()
#ActiveProfiles("test")
#ExtendWith(MockitoExtension.class)
public class GoogleServiceUtilsTest {
#Value("${google.service.account.user}")
private String serviceAccountUser;
#Value("${google.service.account.path}")
private String pathFile;
Set<String> scopesSet = DirectoryScopes.all();
List<String> scopesList = new ArrayList<String>(scopesSet);
#Test
void getCredentialTest() throws GoogleCredentialException {
// Given
GoogleCredentials credentials;
// When
credentials = GoogleServiceUtils.getCredential(serviceAccountUser, scopesList, pathFile);
// Then
assertThat(credentials != null);
assertThat(new HttpCredentialsAdapter(credentials));
}
}
but when I use it in my test method, serviceAccountUser and pathFile variables are always null.
My application-test.yml file is located in 'src/test/resources', and tests are in 'src/test/java/' and content :
google:
service:
account:
user: ...
path: ...
# FED credentials
fed:
url: ...
token: ...
grantType: ...
# Logging
logging:
level:
root: ...
org.springframework: ...
When I use the #Value tag in my app code, all is working. Variables are getting good values from 'src/main/resources/application.yml' file.
After reading comments, I add that the profiles are not useful in my case, but as when I don't use them, it doesn't work, I thought that maybe it came from there and that it is necessary to use them.
I also tried to write the value of the variables hard in the file, and then there is no problem, tests are passing well.
Is someone understanding this problem? Many people seem to have had the same, but I can't find an answer working for me.
Thank you for your answer!

Sorry, cannot reproduce!
Having:
Simplest starter
src/main/resources/application.yml
foo:
bar:
baz: normal
...and
src/test/resources/application-test.yml
foo:
bar:
baz: test
This test passes:
#SpringBootTest
#ActiveProfiles("test") // !
class SomeTest {
#Value("${foo.bar.baz}")
String foo;
#Test
void testProp() {
Assertions.assertEquals("test", foo); //!#
}
}
Either #ExtendWith(MockitoExtension.class) has no effect/does no harm.
Whenever you wonder, where your properties come from
Consult:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
And check (current version):
...considered in the following order:
Default properties (specified by setting SpringApplication.setDefaultProperties).
#PropertySource annotations on your #Configuration classes. Please note ...
Config data (such as application.properties files).
A RandomValuePropertySource that has properties only in random.*.
OS environment variables.
Java System properties (System.getProperties()).
JNDI attributes from java:comp/env.
ServletContext init parameters.
ServletConfig init parameters.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
Command line arguments.
properties attribute on your tests. Available on #SpringBootTest and the test annotations for testing a particular slice of your application.
#TestPropertySource annotations on your tests.
Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
Config data files (3.) are considered in the following order:
Application properties packaged inside your jar (application.properties and YAML variants).
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
Application properties outside of your packaged jar (application.properties and YAML variants).
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).

Related

Binding multiple application.yml from working directory to ConfigurationProperties

Starting with Spring Boot 2.4.0, from what I gather from the reference documentation, I should be able to bind multiple application.yaml files from the working directory to ConfigurationProperties:
Wildcard locations are particularly useful in an environment such as Kubernetes when there are multiple sources of config properties.
For example, if you have some Redis configuration and some MySQL configuration, you might want to keep those two pieces of configuration separate, while requiring that both those are present in an application.properties file. This might result in two separate application.properties files mounted at different locations such as /config/redis/application.properties and /config/mysql/application.properties. In such a case, having a wildcard location of config/*/, will result in both files being processed.
By default, Spring Boot includes config/*/ in the default search locations. The means that all subdirectories of the /config directory outside of your jar will be searched.
It is my understanding that the following test case shows that this does not work as described:
Project hierarchy:
config/
first/
application.yaml
second/
application.yaml
src/
main/
kotlin/
test/
Application.kt
test/
kotlin/
test/
TestCase.kt
config/first/application.yaml
composite:
from-first-file:
it: works
config/second/application.yaml
composite:
from-second-file:
it: works
src/main/kotlin/test/Application.kt
#SpringBootApplication(proxyBeanMethods = false)
#EnableConfigurationProperties(CompositeProperties::class)
class Application
fun main(vararg args: String) {
runApplication<Application>(*args)
}
#ConstructorBinding
#ConfigurationProperties("composite")
data class CompositeProperties(
val fromFirstFile: Map<String, String> = mapOf(),
val fromSecondFile: Map<String, String> = mapOf()
)
src/test/kotlin/test/TestCase.kt
#SpringBootTest
class TestCase {
#Autowired
private lateinit var compositeProperties: CompositeProperties
#Test
fun `first file is bound to configuration properties`() {
assertThat(compositeProperties.fromFirstFile).containsEntry("it", "works")
}
#Test
fun `second file is bound to configuration properties`() {
assertThat(compositeProperties.fromSecondFile).containsEntry("it", "works")
}
}
The first test passes, the second one fails. I tried several variations to rule out edge cases and it looks like the second file is completely overlooked. For instance, I tried setting the spring.application.name in the second file and it is not taken into account either.
If this is indeed a bug, is there a workaround I could use until this is fixed?
As I suspected, this is a bug in 2.4.0. Until the problem is fixed, it can be worked around by setting spring.config.use-legacy-processing=true.

spring-boot application.properties are not overwriten

I have one class:
package com.example.propertyorder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#SpringBootApplication
#PropertySource(value="file:C:\\TEMP\\property-order.properties", ignoreResourceNotFound = true)
public class PropertyOrderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(PropertyOrderApplication.class, args);
Environment env = (Environment) run.getBean("environment");
System.out.println(env.getProperty("my.value"));
System.out.println(env.getProperty("my.value2"));
}
}
and I have one application.properties file:
my.value=application.properties
and I have my external properties file C:\\TEMP\\property-order.properties:
my.value=property-order.properties
my.value2=gotcha
but the result is always:
application.properties
gotcha
instead of:
property-order.properties
gotcha
So it looks like, that spring-boot's application.properties overrules all.
Is there a way to fix it.
The only solution that i found is to not use the application.properties, but a my-app.properties instead and place that one before my external file in the ProperySource-tree:
#PropertySource(value="classpath:my-app.properties")
#PropertySource(value="file:C:\\TEMP\\property-order.properties", ignoreResourceNotFound = true)
Is there a better way, so that I can stay with the application.properties ?
edit:
added missing value2 to the property file
The actual behavior is conform to the Spring Boot documentation :
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order:
....
14.Application properties outside of your packaged jar (application.properties and YAML variants).
15.Application properties packaged inside your jar (application.properties and YAML variants).
16.#PropertySource annotations on your #Configuration classes.
The application.properties (inside or outside the jar) have a higher priority (14 and 15 respectively) than any #PropertySource annotations added in #Configuration class (16).
Is there a better way, so that I can stay with the
application.properties ?
Of course you can use any of the 13 higher priority ways :
1.Devtools global settings properties on your home directory
(~/.spring-boot-devtools.properties when devtools is active).
2.#TestPropertySource annotations on your tests.
3.#SpringBootTest#properties annotation attribute on your tests. Command
line arguments.
4.Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an
environment variable or system property).
5.ServletConfig init parameters.
6.ServletContext init parameters.
7.JNDI attributes from java:comp/env.
8.Java System properties (System.getProperties()).
9.OS environment variables.
10.A RandomValuePropertySource that has properties only in random.*.
11.Profile-specific application properties outside of your packaged jar
(application-{profile}.properties and YAML variants).
12.Profile-specific application properties packaged inside your jar
(application-{profile}.properties and YAML variants).
13.Application properties outside of your packaged jar
(application.properties and YAML variants).
For example renaming property-order.properties to application-order.properties to make it a Spring Boot profile properties and run your application with order as active profile would give it a higher priority and so it should be enough :
#SpringBootApplication
public class PropertyOrderApplication {...}
If you want to override the property defined int the application.properties, you can use the below approach.
#PropertySources({
#PropertySource(value = "classpath:application.properties"),
#PropertySource(value = "file:/user/home/external.properties", ignoreResourceNotFound = true)
})
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
}

SpringBoot: Separate property files

I've created my application.properties file:
spring.config.additional-location=C:\Users\user\
spring.datasource.url=jdbc:postgresql://<db>:<port>/<db>
I need to feed Spring with an additional file located on C:\Users\user\application.properties:
spring.datasource.username=user
spring.datasource.password=password
As you can see I've tried to use spring.config.additional-location property into my application.properties file.
However, bootstrap tells me that no authentication has been provided.
you can use another file name by specifying a spring.config.name environment property. You can also refer to an explicit location by using the spring.config.location environment property (which is a comma-separated list of directory locations or file paths). The following example shows how to specify a different file name:
$ java -jar myproject.jar --spring.config.name=myproject
Reference URL:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
You can specify your alternative properties using #PropertySources like this:
#PropertySources({
#PropertySource({"classpath:application.properties"}),
#PropertySource(value = {"file:${conf-dir}}/application-override.properties" },ignoreResourceNotFound = true)
})
public class AppConfig {
...
The properties in the bottom PropertySource will override properties from first one, if the file exists.
The documentation says to use file:C:/Users/user/
the "file:" or "classpath:" part is important.
There are numerous other ways to do this, too.
- profiles (and per profile application-<profile>.properties)
- #Configuration + #PropertySource
- ...

How to replace properties in application.properties in src/main/resources in spring boot

I am using Spring boot application and having application.properties property file in src/main/resources. It has some properties which needs to replaced by external property file. I will pass the external file location in command line.
Need solution how to replace the properties inside application with external properties.
public static void main(String[] args) throws JMSException, MQException, IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("Application.properties");
Properties properties = new Properties();
properties.load(input);
properties.load(new FileReader(args[0]));
SpringApplication springApplication = new SpringApplication(new Object[]{ChapsSchemeFeed.class});
springApplication.setDefaultProperties(properties);
springApplication.run(args);
}
In this code, I am reading properties from command line and loading them with application.properties residing in application. But when I start, its loading properties from Application.properties. But I want to replace it with property from command line properties file.
From the Spring Boot manual:
Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:
Devtools global settings properties in the $HOME/.config/spring-boot folder when devtools is active.
#TestPropertySource annotations on your tests.
properties attribute on your tests. Available on #SpringBootTest and the test annotations for testing a particular slice of your application.
Command line arguments.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
ServletConfig init parameters.
ServletContext init parameters.
JNDI attributes from java:comp/env.
Java System properties (System.getProperties()).
OS environment variables.
A RandomValuePropertySource that has properties only in random.*.
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
Application properties outside of your packaged jar (application.properties and YAML variants).
Application properties packaged inside your jar (application.properties and YAML variants).
References:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Spring boot on Tomcat with external configuration

I can't find an answer to this question on stackoverflow hence im asking here so I could get some ideas.
I have a Spring Boot application that I have deployed as a war package on Tomcat 8. I followed this guide Create a deployable war file which seems to work just fine.
However the issue I am currently having is being able to externalize the configuration so I can manage the configuration as puppet templates.
In the project what I have is,
src/main/resources
-- config/application.yml
-- config/application.dev.yml
-- config/application.prod.yml
-- logback-spring.yml
So how can I possibly load config/application.dev.yml and config/application.prod.yml externally and still keep config/application.yml ? (contains default properties including spring.application.name)
I have read that the configuration is load in this order,
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
Hence I tried to load the configuration files from /opt/apache-tomcat/lib to no avail.
What worked so far
Loading via export CATALINA_OPTS="-Dspring.config.location=/opt/apache-tomcat/lib/application.dev.yml"
however what I would like to know is,
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
And is there a better method to achieve this ?
You are correct about load order. According to Spring boot documentation
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).
[Note]
You can also use YAML ('.yml') files as an alternative to '.properties'.
This means that if you place your application.yml file to /opt/apache-tomcat/lib or /opt/apache-tomcat/lib/config it will get loaded.
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
However, if you place application.dev.yml to that path, it will not be loaded because application.dev.yml is not filename Spring is looking for. If you want Spring to read that file as well, you need to give it as option
--spring.config.name=application.dev or -Dspring.config.name=application.dev.
But I do not suggest this method.
And is there a better method to achieve this ?
Yes. Use Spring profile-specific properties. You can rename your files from application.dev.yml to application-dev.yml, and give -Dspring.profiles.active=dev option. Spring will read both application-dev.yml and application.yml files, and profile specific configuration will overwrite default configuration.
I would suggest adding -Dspring.profiles.active=dev (or prod) to CATALINA_OPTS on each corresponding server/tomcat instance.
I have finally simplified solution for reading custom properties from external location i.e outside of the spring boot project. Please refer to below steps.
Note: This Solution created and executed windows.Few commands and folders naming convention may vary if you are deploying application on other operating system like Linux..etc.
1. Create a folder in suitable drive.
eg: D:/boot-ext-config
2. Create a .properties file in above created folder with relevant property key/values and name it as you wish.I created dev.properties for testing purpose.
eg :D:/boot-ext-config/dev.properties
sample values:
dev.hostname=www.example.com
3. Create a java class in your application as below
------------------------------------------------------
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
#PropertySource("classpath:dev.properties")
#ConfigurationProperties("dev")
public class ConfigProperties {
private String hostname;
//setters and getters
}
--------------------------------------------
4. Add #EnableConfigurationProperties(ConfigProperties.class) to SpringBootApplication as below
--------------------------------------------
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties.class)
public class RestClientApplication {
public static void main(String[] args) {
SpringApplication.run(RestClientApplication.class, args);
}
}
---------------------------------------------------------
5. In Controller classes we can inject the instance using #Autowired and fetch properties
#Autowired
private ConfigProperties configProperties;
and access properties using getter method
System.out.println("**********hostName******+configProperties.getHostName());
Build your spring boot maven project and run the below command to start application.
-> set SPRING_CONFIG_LOCATION=<path to your properties file>
->java -jar app-name.jar

Resources