Override default Spring-Boot application.properties settings in Junit Test
Asked Answered
E

15

292

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.

Earthly answered 16/4, 2015 at 8:32 Comment(3)
If you need to configure just a few properties, you can use the new @DynamicPropertySource annotation. https://mcmap.net/q/89117/-override-default-spring-boot-application-properties-settings-in-junit-test-with-dynamic-valueBookplate
I used ReflectionTestUtils.setField(myService, "myPropery", myPropertyValue);Vair
Here's how I more carefully used ReflectionTestUtils to replace an configuration provided API key so I could test for supplying a bad API key: https://mcmap.net/q/89118/-how-to-override-properties-by-each-test-in-unittestGabelle
P
373

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 {

}
Plumb answered 16/4, 2015 at 11:45 Comment(11)
That's it. Thanks. Unfortuantly it does not work when used on the ExampleApplication.class so I have to set it on each test class. Is that right?Earthly
It has to go somewhere in the test class's hierarchy, i.e. you could use a common superclass to configure it across a number of different test classes.Plumb
Yes, that would be a proper woraround but as @PropertySource is supported on @Configuration classes why not also @TestPropertySource ;-) Anyway - as expected, the framework will not let me down and I thank you again for answering the question.Earthly
Also note that @TestPropertySource can accept a properties 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.Gonadotropin
You can specify multiple files in an array, and also files on the file system (but remember they might not work on the CI server): @TestPropertySource(locations={"file:C:/dev/...","classpath:test.properties"})Contradictory
Note, that @SpringApplicationConfiguration is already deprecated, and you should use @SpringBootTestFabria
It would be nice if a file src/test/resources/application-test.properties could be used for unit tests.Ridgley
@Ridgley It can be used if you make the test profile active.Plumb
note: @TestPropertySource not load yml file, only support properties fileMadras
But we don't want to have to change source code to modify an application property. Instead, we want to add a command line argument or create another, perhaps temporary, .properties file and force gradle to use that file.Jepson
@Jepson then you have different requirements. I'd ask a new question if I were you.Plumb
R
116

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]

  1. Add src/main/resources/application-test.properties.
  2. 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

Reform answered 10/7, 2017 at 21:23 Comment(15)
Not sure if it is a good idea to have two 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?Earthly
@Earthly Spring is going to guarantee it! Main profile properties are always loaded. Then during the test phase, test properties are loaded, adding/overriding new/existing properties. If you don't like keeping two files with same name, then you can add application-test.properties in src/main/resources and specify test as the active profile in the test case.Reform
Spring does not give a guarantee. The build tool will use the test resources in favour of the main resources during tests. But in case of a test application.properties the main application.properties will be ignored. That is not what I want because the main one contains several useful default values and I only need to override some of them during the test (and I do not want to duplicate the whole file in the test section). See here.Earthly
You're correct, only properties defined in src/test/resources/application.properties are loaded during the test phase, src/main/resources/application.properties is ignored.Reform
If you do not use profiles so far you do not need a dedicated "test" profile. Just name your test properties application-default.properties and they will be considered because you are automatically running the "default" profile (if not declared any other).Earthly
This is incorrect. The file automagically loaded is the default application.properties, not the test one. See https://mcmap.net/q/89119/-junit-test-run-in-eclipse-but-fails-with-gradle-test/1763602Ichthyosaur
@Marco Did you try running the referenced GitHub project?Reform
@Mohnish: the project does not use src/test/resources, it uses src/main/resources. Please read #55298176 and my answerIchthyosaur
@Marco Not sure where you're getting confused. But if you put a file named application.properties under src/test/resources, it is picked up for test cases. However, note that in such a case, only properties defined in that file are available. But as frequently we want main properties, with a few of them overridden for our test case, hence the ActiveProfile approach suggestion.Reform
@Reform if you put a file named application.properties under src/test/resources, it is picked up for test cases This is not true, I tested it in my application and the default application.properties was loaded instead. I had to use @TestPropertySourceIchthyosaur
@MarcoSulla You might be missing something in your setup. Anyway, I've provided code to showcase it in test-props-loading branch of the demo project. It should serve as a proof! Check it out.Reform
How to set a specific property value for each junit test method?Lungki
@Grigory Kislin - if you have a code edit to suggest, please suggest it as a comment. application-test can be placed under src/main/resources, as has been done in the referenced GitHub code sample. It allows users to know that test props can be placed at an alternative location. Also, please don't tag code edits as grammer.Reform
ConfigFileApplicationContextInitializer.class is deprected use ConfigDataApplicationContextInitializer.class instead. Worked for meLottery
For me src/test/application.properties works, but src/main/resources/application-test.properties is ignored, maybe because I'm using Spring Batch.Degroot
L
69

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 { }
Lordling answered 16/4, 2015 at 17:34 Comment(0)
B
66

Another approach suitable for overriding a few properties in your test, if you are using @SpringBootTest annotation:

@SpringBootTest(properties = {"propA=valueA", "propB=valueB"})
Bartolemo answered 20/7, 2019 at 12:0 Comment(2)
does SpringBootTest load the application.properties file?Interradial
@Interradial yesTender
S
25

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:

enter image description here

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.

Stirpiculture answered 19/2, 2020 at 9:17 Comment(3)
I find this approach lacking because defining a profile on your test will remove the option to adjust this on the fly via -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.Bollen
Hey @elonderin, thanks for your comment. If there are configurations that are common to all, i plug them 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?Stirpiculture
This should be the accepted answer. Apparently, test/application.properties doesn't override main/application.properties.Acalia
B
18

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.

Bollen answered 8/8, 2018 at 8:13 Comment(1)
Note that as of spring boot 2.4 the power-developer way is deprected, see spring.io/blog/2020/08/14/…Bollen
B
11

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);
    }
}
Bookplate answered 30/3, 2020 at 20:53 Comment(2)
This should be the top answer. Overriding properties from tests very often needs dynamic values, and just switching to another property file as the other answers won't cut it. Note: The @Testcontainers annotation is not required for this to work.Ambler
This doesn't work when not using Test containersGigantopithecus
D
3

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.

Drusi answered 8/9, 2021 at 11:56 Comment(1)
This worked for me, very well. @TestPropertySource(properties = "spring.config.additional-location=file:/Users/..")Leyte
M
3

This workes for me:

My Test:

@SpringBootTest
@TestPropertySource(properties = "spring.config.additional-location=classpath:application-test.yml")
class EngineApplicationTests {
    @Test
    void contextLoads() {
    }
}

My versions:

plugins {
    id 'org.springframework.boot' version '2.7.1'
    id 'io.spring.dependency-management' version '1.0.12.RELEASE'
    id 'java'
}

group = 'com.kubemachine'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "2021.0.3")
}

The only test dependency in my gradle file:

testImplementation 'org.springframework.boot:spring-boot-starter-test'

I also have this in my build.gradle file:

test {
    useJUnitPlatform()
}

and two properties file:

  • src/main/resources/application.yml
  • src/test/resources/application-test.yml

And in this setup application-test.yml definitely ONLY overrides the values in application.yml. I don't have to repeat the property values from application.yml in application-test.yml. application-test.yml really extends application.yml.

Mirisola answered 3/8, 2022 at 4:41 Comment(0)
G
1

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

Goetz answered 9/11, 2016 at 18:51 Comment(3)
Ignoring application.properties is not an option for me as I only want to override some of the original configuration values in the test.Earthly
I've been looking for a way to have a single test that doesn't load the src/main/resources/application.properties and this is it. Create a file: src/test/resources/empty.properties and add the annotation to the test(s) that should ignore the main properties. @TestPropertySource(properties="spring.config.name=empty")Andyane
How to set a specific property value for each junit test method?Lungki
C
1
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`
Collin answered 18/12, 2019 at 4:15 Comment(0)
I
1

Setup

Using

  • properties declared with .yaml file-ending
  • Spring-Boot 2.7
  • and multiple .yaml files in the classpath

Problem

I noticed that the precedence of @TestPropertySource(locations) was not applied as it would be with .properties files.

The problem I was having was, that Spring kept loading all .yaml properties (especially those from production) and overriding the properties meant for the tests with the values specified for prod. (sigh)

Solution

We come up with the workaround of overwriting the config-scraping mechanism by specifying just my application-test.yaml as the only properties used like so:

@TestPropertySource(properties = "spring.config.location=classpath:/application-test.yaml")

Alternative solution

As mentioned above: with .properties files everything works as expected.

Another solution would be to transpile the .yaml to .properties.

Here is a handy online converter: https://mageddo.com/tools/yaml-converter (note its inability to cope with comments)

Ilex answered 18/8, 2022 at 9:52 Comment(0)
B
0

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);
    }
}
Botswana answered 16/1, 2022 at 10:6 Comment(0)
C
0

If you really want/have to use System.setProperties(...) then use a Junit extension and annotate the test class:

...
@ExtendWith(MyBeforeAllCallback.class)
public SomeTestClass ...

and use the constructor of the callback to set the system properties.

public MyBeforeAllCallback implements BeforeAllCallback {
    public MyBeforeAllCallback() {
        // wicket 9 workaround for java 17 / cglib issue.
        System.setProperty("wicket.ioc.useByteBuddy", "true");
        // ... whatever you want ...
        System.setProperty("whatever", "you need it badly");
    }
    void beforeAll(ExtensionContext context) {
        // ... empty
    }
}
Celery answered 25/1, 2023 at 9:9 Comment(0)
S
-1

You can also create a application.properties file in src/test/resources where your JUnits are written.

Savell answered 23/10, 2018 at 15:58 Comment(3)
How does this help? ^^Stirpiculture
This is what he DOESN'T wants...Vetchling
Thanks I had made application-test.properties in test/resources folder - renamed it to application.properties and it workedLacrimatory

© 2022 - 2024 — McMap. All rights reserved.