How to test code dependent on environment variables using JUnit?

匿名 (未验证) 提交于 2019-12-03 01:52:01

问题:

I have a piece of Java code which uses an environment variable and the behaviour of the code depends on the value of this variable. I would like to test this code with different values of the environment variable. How can I do this in JUnit?

I've seen some ways to set environment variables in Java in general, but I'm more interested in unit testing aspect of it, especially considering that tests shouldn't interfere with each other.

回答1:

The usual solution is to create a class which manages the access to this environmental variable, which you can then mock in your test class.

public class Environment {     public String getVariable() {         return System.getenv(); // or whatever     } }  public class ServiceTest {     private static class MockEnvironment {         public String getVariable() {            return "foobar";         }     }      @Test public void testService() {         service.doSomething(new MockEnvironment());     } } 

The class under test then gets the environment variable using the Environment class, not directly from System.getenv().



回答2:

The library System Rules provides a JUnit Rule for setting environment variables.

public void EnvironmentVariablesTest {   @Rule   public final EnvironmentVariables environmentVariables     = new EnvironmentVariables();    @Test   public void setEnvironmentVariable() {     environmentVariables.set("name", "value");     assertEquals("value", System.getenv("name"));   } } 

Disclaimer: I'm the author of System Rules.



回答3:

Decouple the Java code from the Environment variable providing a more abstract variable reader that you realize with an EnvironmentVariableReader your code to test reads from.

Then in your test you can give an different implementation of the variable reader that provides your test values.

Dependency injection can help in this.



回答4:

I don't think this has been mentioned yet, but you could also use Powermockito:

Given:

package com.foo.service.impl;  public class FooServiceImpl {      public void doSomeFooStuff() {         System.getenv("FOO_VAR_1");         System.getenv("FOO_VAR_2");         System.getenv("FOO_VAR_3");          // Do the other Foo stuff     } } 

You could do the following:

package com.foo.service.impl;  import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic;  import org.junit.Beforea; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.MockitoAnnotations; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner;  @RunWith(PowerMockRunner.class) @PrepareForTest(FooServiceImpl.class) public class FooServiceImpTest {      @InjectMocks     private FooServiceImpl service;      @Before     public void setUp() {         MockitoAnnotations.initMocks(this);          mockStatic(System.class);  // Powermock can mock static and private methods          when(System.getenv("FOO_VAR_1")).thenReturn("test-foo-var-1");         when(System.getenv("FOO_VAR_2")).thenReturn("test-foo-var-2");         when(System.getenv("FOO_VAR_3")).thenReturn("test-foo-var-3");     }      @Test     public void testSomeFooStuff() {                 // Test         service.doSomeFooStuff();          verifyStatic();         System.getenv("FOO_VAR_1");         verifyStatic();         System.getenv("FOO_VAR_2");         verifyStatic();         System.getenv("FOO_VAR_3");     } } 


回答5:

This answer to the question How do I set environment variables from Java? provides a way to alter the (unmodifiable) Map in System.getenv(). So while it doesn't REALLY change the value of the OS environment variable, it can be used for unit testing as it does change what System.getenv will return.



回答6:

In a similar situation like this where I had to write Test Case which is dependent on Environment Variable, I tried following:

  1. I went for System Rules as suggested by Stefan Birkner. Its use was simple. But sooner than later, I found the behavior erratic. In one run, it works, in the very next run it fails. I investigated and found that System Rules work well with JUnit 4 or higher version. But in my cases, I was using some Jars which were dependent on JUnit 3. So I skipped System Rules. More on it you can find here @Rule annotation doesn't work while using TestSuite in JUnit.
  2. Next I tried to create Environment Variable through Process Builder class provided by Java. Here through Java Code we can create an environment variable, but you need to know the process or program name which I did not. Also it creates environment variable for child process, not for the main process.

I wasted a day using the above two approaches, but of no avail. Then Maven came to my rescue. We can set Environment Variables or System Properties through Maven POM file which I think best way to do Unit Testing for Maven based project. Below is the entry I made in POM file.

    org.apache.maven.pluginsmaven-surefire-pluginPropertyValue1PropertyValue2EnvironmentVariableValue1EnvironmentVariableValue2

After this change, I ran Test Cases again and suddenly all worked as expected. For reader's information, I explored this approach in Maven 3.x, so I have no idea on Maven 2.x.



回答7:

I think the cleanest way to do this is with Mockito.spy(). It's a bit more lightweight than creating a separate class to mock and pass around.

Move your environment variable fetching to another method:

@VisibleForTesting String getEnvironmentVariable(String envVar) {     return System.getenv(envVar); } 

Now in your unit test do this:

@Test public void test() {     ClassToTest classToTest = new ClassToTest();     ClassToTest classToTestSpy = Mockito.spy(classToTest);     Mockito.when(classToTestSpy.getEnvironmentVariable("key")).thenReturn("value");     // Now test the method that uses getEnvironmentVariable     assertEquals("changedvalue", classToTestSpy.methodToTest()); } 


回答8:

Well you can use the setup() method to declare the different values of your env. variables in constants. Then use these constants in the tests methods used to test the different scenario.



回答9:

If you want to retrieve informations about the environment variable in Java, you can call the method : System.getenv();. As the properties, this method returns a Map containing the variable names as keys and the variable values as the map values. Here is an example :

    import java.util.Map;  public class EnvMap {     public static void main (String[] args) {         Map env = System.getenv();         for (String envName : env.keySet()) {             System.out.format("%s=%s%n", envName, env.get(envName));         }     } } 

The method getEnv() can also takes an argument. For instance :

String myvalue = System.getEnv("MY_VARIABLE"); 

For testing, I would do something like this :

public class Environment {     public static String getVariable(String variable) {        return  System.getenv(variable); }  @Test  public class EnvVariableTest {       @Test testVariable1(){          String value = Environment.getVariable("MY_VARIABLE1");          doSometest(value);       }      @Test testVariable2(){        String value2 = Environment.getVariable("MY_VARIABLE2");        doSometest(value);       }     } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!