3

I have a class that simply returns environment variables with System.getEnv, and I'm trying to write a JUnit test for it, but I always get just null. I'm trying to use an application-test.yml with all the variables set, and tried many annotations like

@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(locations="classpath:/application-test.yml")

and many many other but still no success. Does anyone knows how this can be done simply?

Vy Do
  • 46,709
  • 59
  • 215
  • 313
Giacomo Giovannini
  • 177
  • 2
  • 2
  • 14
  • why dont you mock System class using PowerMock? Alternatively you could try something like [here](https://stackoverflow.com/questions/318239/how-do-i-set-environment-variables-from-java/496849#496849) – SMA Oct 06 '18 at 11:37
  • I don't think PowerMock is part of the Spring Framework, right? Unfortunately for policy I can use only official spring Classes. I tried the solution in the thread linked but I still get null. – Giacomo Giovannini Oct 06 '18 at 12:45
  • what type of testing are you doing? Is it unit or integration? – SMA Oct 06 '18 at 18:11

3 Answers3

4

There are multiple ways.

Option 1

If you are running tests within your IDE (eclipse or IntelliJ IDEA), you can set variables for the tests in the IDE. If you are using maven, you could set System properties through Surefire plugin - https://maven.apache.org/surefire/maven-surefire-plugin/examples/system-properties.html

<project>
[...]
<build>
<plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.0</version>
    <configuration>
      <systemProperties>
        <property>
          <name>buildDir</name>
          <value>${project.build.outputDirectory}</value>
        </property>
      </systemProperties>
    </configuration>
  </plugin>
</plugins>
</build>
[...]
</project>

Option 2

Using Spring EL

"classpath:config_#{systemProperties['env']}/db.properties" 

More details can be found here - how to read System environment variable in Spring applicationContext

Arun Avanathan
  • 982
  • 4
  • 11
  • 26
1

Spring has nothing to do with environment variables. It can read them but doesn't say any as far as I know, unlike system properties.

Environment variables are meant to exist in environment.

So you have the following options:

Option 1

Use Power Mock that allows mocking out the static method calls. This library is not a part of spring so you can just use it in a scope of test so that it does no effect on production (powermock jar won't be in the list of dependencies for production)

Option 2

Wrap the static calls with some outer class and then mock it with a regular mocking framework / or load the different bean of the same interface since you're using Spring Test anyway. The code will look something like this:

  interface EnvAccessor {
       String getValue(String envVarName);
  }

  public class MyEnvAccessor {
       String getValue(String envVarName) {
          return System.getenv(envVarName);
       }
  }

Option 3

Do nothing in the production code but set the env variable programmatically before the JUnit test starts:

public class SampleTest {

    @BeforeClass
    public static void setupEnvironment() {
       // set the environment here
    }
}

Now, setting the environment from a test in particular and from java code in general is very tricky, because environment variables shouldn't be changed programmatically.

You can read Here about possible workarounds and evaluate them in your program.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
1

The problem you have here is that the SpringJUnitRunner is running at class level before any of the methods in the test class. One solution is to wrap the spring test inside a parent class, and set environment variables in that class.

This is easier to achieve in JUnit 5 with System Stubs - https://github.com/webcompere/system-stubs

Here's a spring example from there (https://github.com/webcompere/system-stubs/blob/master/system-stubs-jupiter/src/test/java/uk/org/webcompere/systemstubs/jupiter/examples/SpringAppWithDynamicPropertiesTest.java):

@ExtendWith(SystemStubsExtension.class)
public class SpringAppWithDynamicPropertiesTest {
    private static WireMockServer wireMock = new WireMockServer(Options.DYNAMIC_PORT);

    // sets the environment before Spring even starts
    @SystemStub
    private static EnvironmentVariables environmentVariables;


    @BeforeAll
    static void beforeAll() {
        // we can manipulate the env vars here
        environmentVariables.set("SERVER_URL", "something");
    }


    @Nested
    @SpringBootTest(classes = {RestApi.class, App.class},
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    class InnerSpringTest {
        @LocalServerPort
        private int serverPort;

        @Test
        void someTest() {
            // the spring app is testable with the given env

        }
    }
}

I think the same can also be achieved in JUnit 4 with an Enclosed runner for the outer class, and use of the EnvironmentVariablesRule as a static member, with environment variables set in the outer class's @BeforeClass method.

Ashley Frieze
  • 4,993
  • 2
  • 29
  • 23