如何使用JUnittesting依赖于环境variables的代码?

我有一段使用环境variables的Java代码,代码的行为取决于这个variables的值。 我想用不同的环境variables值来testing这段代码。 我如何在JUnit中做到这一点?

我已经看到了一些通常在Java中设置环境variables的方法 ,但我更关心它的unit testing方面,特别是考虑到testing不应该相互干扰。

通常的解决scheme是创build一个类来pipe理对这个环境variables的访问,然后你可以在你的testing类中进行模拟。

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()); } } 

被testing的类然后使用Environment类获取环境variables,而不是直接从System.getenv()获取。

库系统规则提供了用于设置环境variables的JUnit规则。

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

免责声明:我是“系统规则”的作者。

将Java代码从Environmentvariables中解耦出来,提供一个更为抽象的variables读取器,通过您的代码的EnvironmentVariableReader来实现读取。

然后在您的testing中,您可以给出提供testing值的variables读取器的不同实现。

dependency injection可以帮助这个。

这个问题的答案 如何从Java设置环境variables? 提供了一种在System.getenv()中更改(不可修改)Map的方法。 所以虽然它并没有真正改变OS环境variables的值,但它可以用于unit testing,因为它改变了System.getenv的返回值。

我不认为这已被提及,但你也可以使用Powermockito

鉴于:

 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 } } 

你可以做到以下几点:

 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"); } } 

在类似的情况下,我不得不编写依赖于环境variables的 testing用例 ,我尝试了以下内容:

  1. 我按照Stefan Birkner的build议去做 系统规则 。 它的使用很简单。 但迟早,我发现行为不稳定。 在一次运行中,它运行,在下一次运行中失败。 我调查了一下,发现系统规则和JUnit 4或者更高版本一起工作的很好。 但在我的情况下,我正在使用一些依赖于JUnit 3的 Jars。 所以我跳过了系统规则 。 更多关于它你可以在这里find@Rule注解在JUnit中使用TestSuite时不起作用 。
  2. 接下来,我尝试通过Java提供的Process Builder类创build环境variables 。 这里通过Java代码我们可以创build一个环境variables,但是你需要知道我没有的过程程序名。 它也为subprocess创build环境variables,而不是为主进程创build。

我用上述两种方法浪费了一天的时间,但无济于事。 然后Maven来救我。 我们可以通过Maven POM文件来设置环境variables系统属性 ,我认为最好的方法是对基于Maven的项目进行unit testing 。 以下是我在POM文件中创build的条目。

  <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <systemPropertyVariables> <PropertyName1>PropertyValue1</PropertyName1> <PropertyName2>PropertyValue2</PropertyName2> </systemPropertyVariables> <environmentVariables> <EnvironmentVariable1>EnvironmentVariableValue1</EnvironmentVariable1> <EnvironmentVariable2>EnvironmentVariableValue2</EnvironmentVariable2> </environmentVariables> </configuration> </plugin> </plugins> </build> 

在这个改变之后,我再次运行了testing用例 ,突然间都按预期工作。 对于读者的信息,我在Maven 3.x中探索了这个方法,所以我对Maven 2.x一无所知

我认为最简单的方法是使用Mockito.spy()。 这比创build一个单独的类来模拟和传递更轻量级。

将您的环境variables提取到另一个方法:

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

现在在你的unit testing中这样做:

 @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()); } 

那么你可以使用setup()方法来声明env的不同值。 常量中的variables。 然后在用于testing不同场景的testing方法中使用这些常量。

如果要检索Java中有关环境variables的信息,可以调用方法: System.getenv(); 。 作为属性,此方法返回一个包含variables名称作为键和variables值作为映射值的映射。 这里是一个例子:

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

getEnv()方法也可以带参数。 例如 :

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

为了testing,我会做这样的事情:

 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); } }