How to set environment variable or system property in spring tests?

JavaSpringEnvironment VariablesSpring Test

Java Problem Overview


I'd like to write some tests that check the XML Spring configuration of a deployed WAR. Unfortunately some beans require that some environment variables or system properties are set. How can I set an environment variable before the spring beans are initialized when using the convenient test style with @ContextConfiguration?

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
public class TestWarSpringContext { ... }

If I configure the application context with annotations, I don't see a hook where I can do something before the spring context is initialized.

Java Solutions


Solution 1 - Java

You can initialize the System property in a static initializer:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
public class TestWarSpringContext {

    static {
        System.setProperty("myproperty", "foo");
    }

}

The static initializer code will be executed before the spring application context is initialized.

Solution 2 - Java

The right way to do this, starting with Spring 4.1, is to use a @TestPropertySource annotation.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
@TestPropertySource(properties = {"myproperty = foo"})
public class TestWarSpringContext {
    ...    
}

See @TestPropertySource in the Spring docs and Javadocs.

Solution 3 - Java

One can also use a test ApplicationContextInitializer to initialize a system property:

public class TestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
{
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext)
    {
        System.setProperty("myproperty", "value");
    }
}

and then configure it on the test class in addition to the Spring context config file locations:

@ContextConfiguration(initializers = TestApplicationContextInitializer.class, locations = "classpath:whereever/context.xml", ...)
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest
{
...
}

This way code duplication can be avoided if a certain system property should be set for all the unit tests.

Solution 4 - Java

All of the answers here currently only talk about the system properties which are different from the environment variables that are more complex to set, esp. for tests. Thankfully, below class can be used for that and the class docs has good examples

EnvironmentVariables.html

A quick example from the docs, modified to work with @SpringBootTest

@SpringBootTest
public class EnvironmentVariablesTest {
   @ClassRule
   public final EnvironmentVariables environmentVariables = new EnvironmentVariables().set("name", "value");

   @Test
   public void test() {
     assertEquals("value", System.getenv("name"));
   }
 }

Solution 5 - Java

If you want your variables to be valid for all tests, you can have an application.properties file in your test resources directory (by default: src/test/resources) which will look something like this:

MYPROPERTY=foo

This will then be loaded and used unless you have definitions via @TestPropertySource or a similar method - the exact order in which properties are loaded can be found in the Spring documentation chapter 24. Externalized Configuration.

Solution 6 - Java

You can set the System properties as VM arguments.

If your project is a maven project then you can execute following command while running the test class:

mvn test -Dapp.url="https://stackoverflow.com"

Test class:

public class AppTest  {
@Test
public void testUrl() {
    System.out.println(System.getProperty("app.url"));
    }
}

If you want to run individual test class or method in eclipse then :

  1. Go to Run -> Run Configuration

  2. On left side select your Test class under the Junit section.

  3. do the following :

[![enter image description here][1]][1] [1]: https://i.stack.imgur.com/kgihi.png

Solution 7 - Java

For Unit Tests, the System variable is not instantiated yet when I do "mvn clean install" because there is no server running the application. So in order to set the System properties, I need to do it in pom.xml. Like so:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.21.0</version>
    <configuration>
        <systemPropertyVariables>
            <propertyName>propertyValue</propertyName>
	        <MY_ENV_VAR>newValue</MY_ENV_VAR>
	        <ENV_TARGET>olqa</ENV_TARGET>
            <buildDirectory>${project.build.directory}</buildDirectory>
        </systemPropertyVariables>
    </configuration>
</plugin>

Solution 8 - Java

For springboot, here would be the simplest way to do it in my opinion use the @SpringBootTest annotation you can in java:

@SpringBootTest(
    properties = { "spring.application.name=example", "ENV_VARIABLE=secret" }
)
public class ApplicationTest {

    // Write your tests here

}

Or in kotlin you can do:

@SpringBootTest(
    properties = ["spring.application.name=example", "ENV_VARIABLE=secret"]
)
internal class ApplicationKTest {

    // Write your tests here

}

And that's it your test should run overriding the properties with the one you have define in the annotation. Let's say you had an application.yml looking like that:

spring:
  application:
    name: "app"

db:
  username: "user"
  password: ${ENV_VARIABLE:default}

Then during the test it would be:

  • The spring property spring.application.name will return the value "example"
  • The environment variable ENV_VARIABLE will return "secret", so if you use the value db.password in your code it would return "secret".

Solution 9 - Java

If you have a lot of test classes (IT tests that startup tomcat/server), and the tests are failing, you need to set the system property using System.setProperty("ccm.configs.dir", configPath); Since you need to make sure that is set before spring starts, you need to put it in a static context in a class. And to make sure any test that may depend on it gets this set system property, define a simple config class in your test folder setting up that variable. P.S in my case the env variable that was needed was "ccm.configs.dir" Here is what i added in my test folder,

@Configuration
public class ConfigLoader {
  static {
    
    System.setProperty("ccm.configs.dir", "path/to/the/resource");
  }

}

And all my integration test classes were able to get that variable already set by the time they are run.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHans-Peter St&#246;rrView Question on Stackoverflow
Solution 1 - JavaJimmy PraetView Answer on Stackoverflow
Solution 2 - JavaRamanView Answer on Stackoverflow
Solution 3 - JavaanreView Answer on Stackoverflow
Solution 4 - JavaHarish GokavarapuView Answer on Stackoverflow
Solution 5 - JavablalasaadriView Answer on Stackoverflow
Solution 6 - JavaJoby Wilson MathewsView Answer on Stackoverflow
Solution 7 - JavaGeneView Answer on Stackoverflow
Solution 8 - JavaSylhareView Answer on Stackoverflow
Solution 9 - JavamykeyView Answer on Stackoverflow