I have a simple unit test, and I'd like for the unit tests to load up a test property file that I've placed in src/test/resources/test.properties instead of the property file in src/main/resources/application.properties.
My test class is set up as follows:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#TestPropertySource(locations = "classpath:src/test/resources/properties-test.yml")
public class SecurityConfigurationTest {
#MockBean
KafkaConfiguration kafkaConfiguration;
#MockBean
KafkaMonitor kafkaMonitor;
#MockBean
BuildProperties buildProperties;
#Autowired
MockMvc mockMvc;
#Test
public void unauthorizedUser_shouldNotAccessHomePage() throws Exception{
mockMvc
.perform(get("/"))
.andExpect(status().isUnauthorized());
}
}
My project file structure is as shown below:
But I am getting the following error, suggesting I'm not specifying this property file correctly:
Caused by: java.io.FileNotFoundException: class path resource [src/test/resources/test.properties] cannot be opened because it does not exist
How do can I manage to get the test.properties file to be used instead of the application.properties file for my unit tests?
In pom.xml add a profiles tag which specifies which properties file should the program choose when it runs.
<profiles>
<profile>
<id>test</id>
<properties>
<activatedProperties>test</activatedProperties>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
Note: name of your properties file must be application-test.properties.
This will make your test properties active but to access it you need to configure your test resource folder and let the intellij know 'use this when running.
In Intellij, you can change your resource folder with which you run your project, so you need to....
Select test resource folder.
Right click on it. In the opened menu, select Modify Run Configuration and create a new configuration.
This means a new Run Configuration is created for running test resource folder.
Run the configuration.
Maven by default will package all files in the src/test/resources/ folder to the classpath. So to refer it by the classpath , it should be :
#TestPropertySource(locations = "classpath:test.properties")
Alternatively, if you want to refer it by the system file path , you can change it to :
#TestPropertySource(locations = "file:src/test/resources/test.properties")
Under the cover , its uses ResourceLoader to load the file. See this for more details.
Related
In my Spring Boot program I'm getting a failure due to a bad property value on load. In particular, it uses the DB2 hibernate dialect but it's not defined in the property file I thought I was using.
Assuming no annotations, where does Spring look for the properties file? Yes I know it normally resides in src/main/resources/application.properties
What if I have a property in my test cases; does it ignore the one in main and use the one in test? Or does it start with the main version and let the test one override the main where it applies?
Does the application profile affect the property file used? Some people use the same application.properties file name in both main and test.
If I do have a TestSource annotation with a class path location, does it still augment it with something somewhere else?
Finally, how can I get Spring to tell me everywhere it looked for properties and not just one of them?
#Woodsman
It's possible to use many settings in each profile/environment in spring application on src/main/resources/ folder
application-dev.properties
application-local.properties
application-onlytests.properties
application-prd.properties
application.properties
the name after hyphen will be the name profile got by spring
Ex: application-{myenviroment}.properties
When you start spring application will be used application.properties by default.
You can tell spring to use an specific properties passing the environment in one of ways below:
Putting spring.profiles.active=prd inside application.properties file
Passing by parameters when start spring app --spring.profiles.active=local
Running your jar on command line java -jar myjar.jar --spring.profiles.active=dev
Setting an environment var in your machine/docker/container SET SPRING_ACTIVES_PROFILE=local
There are other ways using annotations on beans, passing jvm arguments and others
If you need run your tests in a specific configuration ( vars, database, settings ), It's possible to pass which .properties will be used
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#SpringBootTest
#TestPropertySource(locations = "classpath:application-onlytests.properties")
public class RunTest_from_onlytests_properties {
#Autowired
private MockMvc mockMvc;
#Test // org.junit.jupiter.api.Test
public void test() throws Exception{
// ...
}
}
I have a spring boot application that I can package in a war that I want to deploy to different environments. To automate this deployment it'd be easier to have the configuration file externalized.
Currently everything works fine with a application.properties file in src/main/resources. Then I use ´mvn install´ to build a war deployable to tomcat.
But I would like to use a .yml file that does not need to be present on mvn install but that would be read from during deployment of the war and is in the same or a directory relative to my war.
24. externalized configuration shows where spring boot will look for files and 72.3 Change the location of external properties of an application gives more detail on how to configure this but I just do not understand how to translate this to my code.
My application class looks like this:
package be.ugent.lca;
Updated below
Do I need to add a #PropertySource to this file? How would I refer to a certain relative path?
I feel like it's probably documented in there as most spring boot documentation but I just don't understand how they mean me to do this.
EDIT
Not sure if this should be a separate issue but I think it's still related.
Upon setting the os variable the error of yaml file not found went away. Yet I still get the same error again as when I had no application .properties or .yml file.
Application now looks like this:
#Configuration
**#PropertySource("file:${application_home}/application.yml")**
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
The application_home OS variable
$ echo $application_home
C:\Masterproef\clones\la15-lca-web\rest-service\target
My application.yml file(part it complains about):
sherpa:
package:
base: be.ugent.lca
Error upon java -jar *.war
All variations upon:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'sherpa.package.base' in string value "${sherpa.package.base}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:808)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1027)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 142 more
Using external properties files
The answer lies in the Spring Boot Docs, I'll try to break it down for you.
First of all, no you should not use #PropertySource when working with Yaml configuration, as mentioned here under the Yaml shortcomings :
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
So, how to load propery files? That is explained here Application Property Files
One is loaded for you: application.yml , place it in one of the directories as mentioned in the link above. This is great for your general configuration.
Now for your environment specific configuration (and stuff like passwords) you want to use external property files, how to do that is also explained in that section :
If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).
So you use the spring.config.location environment property.
Imagine you have an external config file: application-external.yml in the conf/ dir under your home directory, just add it like this:
-Dspring.config.location=file:${home}/conf/application-external.yml as a startup parameter of your JVM.
If you have multiple files, just seperate them with a comma. Note that you can easily use external properties like this to overwrite properties, not just add them.
I would advice to test this by getting your application to work with just your internal application.yml file , and then overwrite a (test) property in your external properties file and log the value of it somewhere.
Bind Yaml properties to objects
When working with Yaml properties I usually load them with #ConfigurationProperties, which is great when working with for example lists or a more complex property structure. (Which is why you should use Yaml properties, for straightforward properties you are maybe better of using regular property files). Read this for more information: Type-Safe Configuration properties
Extra: loading these properties in IntelliJ, Maven and JUnit tests
Sometimes you want to load these properties in your maven builds or when performing tests. Or just for local development with your IDE
If you use IntelliJ for development you can easily add this by adding it to your Tomcat Run Configuration : "Run" -> "Edit Configurations" , select your run configuration under "Tomcat Server" , check the Server tab and add it under "VM Options".
To use external configuration files in your Maven build : configure the maven surefire plugin like this in your pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.config.location=file:${home}/conf/application-external.yml</argLine>
</configuration>
</plugin>
When running JUnit tests in IntelliJ:
Run → Edit Configurations
Defaults → JUnit
add VM Options -> -ea -Dspring.config.location=file:${home}/conf/application-external.yml
Yes, you need to use #PropertySource as shown below.
The important point here is that you need to provide the application_home property (or choose any other name) as OS environment variable or System property or you can pass as a command line argument while launching Spring boot. This property tells where the configuration file (.properties or .yaml) is exactly located (example: /usr/local/my_project/ etc..)
#Configuration
#PropertySource("file:${application_home}config.properties")//or specify yaml file
#ComponentScan({"be.ugent.lca","be.ugent.sherpa.configuration"})
#EnableAutoConfiguration
#EnableSpringDataWebSupport
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
There is a very simple way to achieve this.
Inside your original application.properties file you can just specify the following line:
spring.config.import=file:Directory_To_The_File/Property_Name.properties
It will automatically sync all the properties from the external property file.
Now lets say that you have a situation where you need to get properties from multiple property files. In that case, you can mention the same line in the external property file which in turn will take the remaining properties from the second property file and so on.
Consider the following example.
application.properties:
spring.config.import=file:Resources/Custom1.properties
Custom1.properties:
server.port=8090
.
.
.
spring.config.import=file:Resources/Custom2.properties
One of the easiest way to use externalized property file using system environment variable is, in application.properties file you can use following syntax:
spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PORT}
Now, declare above used environment variables,
export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"
This way you can use different value for same variable in different environments.
Use below code in your boot class:
#PropertySource({"classpath:omnicell-health.properties"})
use below code in your controller:
#Autowired
private Environment env;
I have 2 properties files stored in resources folder: qa.properties and dev.properties.
I specified Maven property 'environment', this is the name of property file that should be loaded.
<properties>
<environment>qa</environment>
</properties>
I want to load required properties according to the maven variable, but #PropertySource doesn't see maven variables. How to pass 'environment' property from Maven to the #PropertySource?
#Configuration
#PropertySource("classpath:${environment}.properties")
public class Config {
#Autowired
private Environment env;
public Environment getEnv() {
return env;
}
public void setEnv(Environment env) {
this.env = env;
}
}
Maybe you can use profile-specific properties like application-qa.properties for this purpose and activate it in application.properties with spring.profiles.active=qa here more https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
Don't do this at build time using a Maven property. This approach forces you to build an artifact for every environment which is not a good practice.
A better approach is to build only one artifact and/or application to be deployed in any environment (qa, stage, load-test, prod, ....)
One way is to name your property files application-test.properties, application-xxxxx.properties and run you application as:
java -Dspring.profiles.active=qa myapp.jar
No need for the Config class you described earlier if you follow this approach.
This should get you started. Notice you would have to include property files for different environments but this could also be worked out. Take a look at the 12 factor-app.
For this purpose you have to specify your environment property as System property. Maven Surefire Plugin can help you with that:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<systemPropertyVariables>
<environment>QA</environment>
</systemPropertyVariables>
</configuration>
</plugin>
I'm trying to read a file from classpath like this in my unit test:
#Value("classpath:state.json")
Resource stateFile;
I have state.json file in src/test/resources directory.
When I try to read this file using stateFile.getInputStream(), it doesn't return any contents. What am I doing wrong?
My test class is annotated like this
#RunWith(SpringRunner.class)
#SpringBootTest
I can see that the code fails if I try with a incorrect file. So I think its seeing the file in classpath but for some reason not reading contents.
I just ran into this. I'm using Maven. I took a look at my target/test-classes folder and my resource file wasn't in there (even though it was in my src/test/resources folder).
I ran mvn clean install and then rechecked my target/test-classes folder and the resource file was now there. After that, my test was able to find the file and the test worked.
So it seems that your resources aren't copied until you do a mvn clean. JUnit is looking in the classpath built by maven and until the file actually makes it into the target/test-classes folder, JUnit won't be able to find it.
You cant access a #Value resource unless its a property defined.
It should be this way.
#Value("${stateJsonPath}")
Resource stateFile;
If you have to get the resource from hardcoded path then use this way.
Resource stateFile = new ClassPathResource("state.json");
Sharing the working solution for posterity. Files present in the classpath can be read using org.springframework.core.io.ResourceLoader. For instance, I have a sample data file called posts.json under directory src/test/java/resources/data and I have to load it during the test case execution as part of #Before
#ActiveProfiles("test")
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleTest {
#Autowired
private ResourceLoader resourceLoader = null;
#BeforeEach
void init() {
File dataFile = resourceLoader.getResource("classpath:data/posts.json").getFile();
...
...
}
}
NOTE: the file should present in the classpath when you use the classifier classpath:
This should simply work, notice that the path starts with a dot, indicating current directory;
#Test
public void testFile() {
String path = "./src/test/resources";
String fileName = "test.zip";
File file = new File(path, fileName);
assertTrue(file.exists());
}
I have a maven based Spring 3.0 project. The Spring configuration file for the web application is located at /<proj>/src/main/webapp/WEB-INF/spring/webmvc-config.xml.
Now I want to have a JUnit test that checks if the context can be started. But I did not knwo how to specify the location of that file in the #ContextConfiguration tag in a maven correct way.
I am doing:
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional()
#ContextConfiguration({
"classpath:META-INF/spring/application-context.xml",
"file:src/main/webapp/WEB-INF/spring/webmvc-config.xml",
})
public class SpringMvcContextTest {
But I am pretty sure that I should not refer to the maven src directory.
If you are on maven why don't you choose resource directory for this, and use classpah:file.xml