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.
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 {
}
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
application.properties
files on the classpath (one in src/main/resources
and one in src/test/resources
). Who guarantees that both will be taken and which one will be taken first?
application-test.properties
in src/main/resources
and specify test
as the active profile in the test case.
src/test/resources/application.properties
are loaded during the test phase, src/main/resources/application.properties
is ignored.
application-default.properties
and they will be considered because you are automatically running the "default" profile (if not declared any other).
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 { }
Another approach suitable for overriding a few properties in your test, if you are using @SpringBootTest
annotation:
@SpringBootTest(properties = {"propA=valueA", "propB=valueB"})
SpringBootTest
load the application.properties file?
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:
https://i.stack.imgur.com/OLBez.png
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.
-Dspring.profiles.active=...
in CI and other environments which might need other configs that wou might not want to override via -D
as this becomes very tedious. After all spinning up spring context should only be needed for integration tests and those are likely to depend on external resources which are different for the environements they run in.
application.properties
. Thereafter, if i develop on local
and doing test
, spring.profiles.active=local, test
. If i am on pre-prod, it becomes spring.profiles.active=pre-prod, test
. Is this what you wanted?
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.
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);
}
}
@Testcontainers
annotation is not required for this to work.
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.
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
application.properties
is not an option for me as I only want to override some of the original configuration values in the test.
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`
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);
}
}
You can also create a application.properties file in src/test/resources where your JUnits are written.
Success story sharing
@TestPropertySource
can accept aproperties
argument to overwrite some property inline, such as@TestPropertySource(properties = "myConf.myProp=valueInTest")
, it's useful in case that you don't want a totally brand new property file.@TestPropertySource(locations={"file:C:/dev/...","classpath:test.properties"})
@SpringApplicationConfiguration
is already deprecated, and you should use@SpringBootTest
src/test/resources/application-test.properties
could be used for unit tests.