Override default Spring-Boot application.properties settings in Junit Test
JavaUnit TestingSpring BootJava Problem Overview
I have a Spring-Boot application where the default properties are set in an application.properties
file in the classpath (src/main/resources/application.properties).
I would like to override some default settings in my JUnit test with properties declared in a test.properties
file (src/test/resources/test.properties)
I usualy have a dedicated Config Class for my Junit Tests, e.g.
package foo.bar.test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(CoreConfig.class)
@EnableAutoConfiguration
public class TestConfig {
}
I first thought that using @PropertySource("classpath:test.properties")
in the TestConfig class would do the trick, but these properties will not overwrite the application.properties settings (see Spring-Boot Reference Doc - 23. Externalized Configuration).
Then I tried to use -Dspring.config.location=classpath:test.properties
when invoking the test. That was successful - but I don't want to set this system property for each test execution. Thus I put it in the code
@Configuration
@Import(CoreConfig.class)
@EnableAutoConfiguration
public class TestConfig {
static {
System.setProperty("spring.config.location", "classpath:test.properties");
}
}
which unfortunatly was again not successful.
There must be a simple solution on how to override application.properties
settings in JUnit tests with test.properties
that I must have overlooked.
Java Solutions
Solution 1 - Java
You can use @TestPropertySource
to override values in application.properties
. From its javadoc:
> test property sources can be used to selectively override properties defined in system and application property sources
For example:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ExampleApplication.class)
@TestPropertySource(locations="classpath:test.properties")
public class ExampleApplicationTests {
}
Solution 2 - Java
Spring Boot automatically loads src/test/resources/application.properties
, if following annotations are used
@RunWith(SpringRunner.class)
@SpringBootTest
So, rename test.properties
to application.properties
to utilize auto configuration.
> If you only need to load the properties file (into the Environment) you can also use the following, as explained here > @RunWith(SpringRunner.class) @ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
[Update: Overriding certain properties for testing]
- Add
src/main/resources/application-test.properties
. - Annotate test class with
@ActiveProfiles("test")
.
This loads application.properties
and then application-test.properties
properties into application context for the test case, as per rules defined here.
Demo - https://github.com/mohnish82/so-spring-boot-testprops
Solution 3 - Java
You can also use meta-annotations to externalize the configuration. For example:
@RunWith(SpringJUnit4ClassRunner.class)
@DefaultTestAnnotations
public class ExampleApplicationTests {
...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@SpringApplicationConfiguration(classes = ExampleApplication.class)
@TestPropertySource(locations="classpath:test.properties")
public @interface DefaultTestAnnotations { }
Solution 4 - Java
Another approach suitable for overriding a few properties in your test, if you are using @SpringBootTest
annotation:
@SpringBootTest(properties = {"propA=valueA", "propB=valueB"})
Solution 5 - Java
TLDR:
So what I did was to have the standard src/main/resources/application.properties
and also a src/test/resources/application-default.properties
where i override some settings for ALL my tests.
For power-developers:
In order to change/use even more easily different spring profiles, I have a now an application-default.yaml
that declares the profiles I want to use.
This file is not committed, so that each developer may choose his way of activating profiles and needs (e.g. feature) he/she is working on.
spring:
profiles:
include:
- local
- devlocal
- wip
# - kafka@docker
---
spring.profiles: wip
# ... overriding properties
Whole Story
I ran into the same problem and was not using profiles either so far. It seemed to be bothersome to have to do it now and remember declaring the profile -- which can be easily forgotten.
The trick is, to leverage that a profile specific application-<profile>.properties
overrides settings in the general profile. See https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties.
Solution 6 - Java
If you are like me and you have the same application.properties
in src/main/resources
and src/test/resources
, and you are wondering why the application.properties
in your test folder is not overriding the application.properties
in your main resources, read on...
Simple explanation:
If you have application.properties
under src/main/resources
and the same application.properties
under src/test/resources
, which application.properties
gets picked up, depends on how you are running your tests. The folder structure src/main/resources
and src/test/resources
, is a Maven architectural convention, so if you run your test like mvnw test
or even gradlew test
, the application.properties
in src/test/resources
will get picked up, as test classpath will precede main classpath. But, if you run your test like Run as JUnit Test
in Eclipse/STS, the application.properties
in src/main/resources
will get picked up, as main classpath precedes test classpath.
You can check it out by opening the menu bar Run > Run Configurations > JUnit > *your_run_configuration* > Click on "Show Command Line"
.
You will see something like this:
> XXXbin\javaw.exe -ea -Dfile.encoding=UTF-8 -classpath
> XXX\workspace-spring-tool-suite-4-4.5.1.RELEASE\project_name\bin\main;
> XXX\workspace-spring-tool-suite-4-4.5.1.RELEASE\project_name\bin\test;
>
Do you see that classpath xxx\main comes first, and then xxx\test? Right, it's all about classpath :-)
Side-note: Be mindful that properties overridden in the Launch Configuration(In Spring Tool Suite IDE, for example) takes priority over application.properties.
Change the order:
Now, everything is configurable in Spring. You can change the build classpath, so that xxx\test comes first, and then xxx\main.
Simply go to Project > Properties > Java Build Path > Order and Export
, change the build class path order by putting any of the test folder first such as:
And that's it!
Better solution
A better solution though, when testing, would be to activate the src/test/resources/application-{profile}.properties
(where profile
can be test), such as the following in src/main/resources/application.properties
:
> spring.profiles.active=test
This is neater, and gives you complete control on what profile to activate when doing what.
Solution 7 - Java
If you're using Spring 5.2.5 and Spring Boot 2.2.6 and want to override just a few properties instead of the whole file. You can use the new annotation: @DynamicPropertySource
@SpringBootTest
@Testcontainers
class ExampleIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>();
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.neo4j.uri", neo4j::getBoltUrl);
}
}
Solution 8 - Java
Otherwise we may change the default property configurator name, setting the property spring.config.name=test
and then having class-path resource
src/test/test.properties
our native instance of org.springframework.boot.SpringApplication
will be auto-configured from this separated test.properties, ignoring application properties;
Benefit: auto-configuration of tests;
Drawback: exposing "spring.config.name" property at C.I. layer
ref: http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
> spring.config.name=application # Config file name
Solution 9 - Java
I just configured min as the following :
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# changing the name of my data base for testing
spring.datasource.url= jdbc:h2:mem:mockedDB
spring.datasource.username=sa
spring.datasource.password=sa
# in testing i don`t need to know the port
#Feature that determines what happens when no accessors are found for a type
#(and there are no annotations to indicate it is meant to be serialized).
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false`enter code here`
Solution 10 - Java
I think you can also use this:
@TestPropertySource(properties = "spring.config.additional-location=classpath:application-test.yml")
when custom config locations are configured by using spring.config.additional-location, they are used in addition to the default locations.
The file will have precedence
Please refer here for more details.
Solution 11 - Java
You can create a spring.factories file in src/test/resources/META-INF and a EnvironmentPostProcessor Implementation class in src/test/java.
spring.factories
like
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.test.YourTestPropertiesConfig
YourTestPropertiesConfig.java
like
package com.example.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
public class YourTestPropertiesConfig implements EnvironmentPostProcessor {
private static final Map<String, Object> testProperties = new HashMap<>();
private static final Set<String> testPropertiesFile = new HashSet<>();
static {
//Add the properties you need to take effect globally in the test directly here.
testProperties.put("spring.jackson.time-zone", "GMT");
testPropertiesFile.add("classpath:test.properties");
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
environment.getPropertySources().addFirst(new MapPropertySource("TestProperties", testProperties));
for (String location : testPropertiesFile) {
try {
environment.getPropertySources().addFirst(new ResourcePropertySource(location));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void addProperty(String key, Object value) {
testProperties.put(key, value);
}
public static void addProperty(String location) {
testPropertiesFile.add(location);
}
}
Solution 12 - Java
You can also create a application.properties file in src/test/resources where your JUnits are written.