Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用JUnit测试依赖于环境变量的代码?_Java_Unit Testing_Testing_Junit_Environment Variables - Fatal编程技术网

Java 如何使用JUnit测试依赖于环境变量的代码?

Java 如何使用JUnit测试依赖于环境变量的代码?,java,unit-testing,testing,junit,environment-variables,Java,Unit Testing,Testing,Junit,Environment Variables,我有一段使用环境变量的Java代码,代码的行为取决于这个变量的值。我想用环境变量的不同值来测试这段代码。在JUnit中如何执行此操作 一般来说,我已经看到了,但我对它的单元测试方面更感兴趣,特别是考虑到测试不应该相互干扰。将Java代码与环境变量解耦,提供一个更抽象的变量读取器,您可以通过环境变量读取器实现测试代码的读取 然后,在测试中,您可以为提供测试值的变量读取器提供不同的实现 依赖项注入可以在这方面有所帮助。通常的解决方案是创建一个类来管理对该环境变量的访问,然后可以在测试类中对其进行模拟

我有一段使用环境变量的Java代码,代码的行为取决于这个变量的值。我想用环境变量的不同值来测试这段代码。在JUnit中如何执行此操作


一般来说,我已经看到了,但我对它的单元测试方面更感兴趣,特别是考虑到测试不应该相互干扰。

将Java代码与环境变量解耦,提供一个更抽象的变量读取器,您可以通过环境变量读取器实现测试代码的读取

然后,在测试中,您可以为提供测试值的变量读取器提供不同的实现


依赖项注入可以在这方面有所帮助。

通常的解决方案是创建一个类来管理对该环境变量的访问,然后可以在测试类中对其进行模拟

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

然后,被测试的类使用环境类而不是直接从System.getenv()获取环境变量。

该问题提供了一种在System.getenv()中更改(不可修改)映射的方法。因此,虽然它不会真正更改OS环境变量的值,但它可以用于单元测试,因为它会更改返回的值。

您可以使用setup()方法声明环境变量的不同值。常量中的变量。然后在用于测试不同场景的测试方法中使用这些常量。

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

    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));
        }
    }
}
对于测试,我会这样做:

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

我认为最干净的方法是使用Mockito.spy()。它比创建单独的类来模拟和传递要轻一些

将环境变量回迁移动到另一个方法:

@VisibleForTesting
String getEnvironmentVariable(String envVar) {
    return System.getenv(envVar);
}
现在在单元测试中,请执行以下操作:

@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());
}
该库有一个使用环境变量设置环境变量的方法

import static com.github.stefanbirkner.systemlambda.SystemLambda.*;

public void EnvironmentVariablesTest {
  @Test
  public void setEnvironmentVariable() {
    String value = withEnvironmentVariable("name", "value")
      .execute(() -> System.getenv("name"));
    assertEquals("value", value);
  }
}
对于Java 5到7,该库有一个JUnit规则,称为
EnvironmentVariables

import org.junit.contrib.java.lang.system.EnvironmentVariables;

public class EnvironmentVariablesTest {
  @Rule
  public final EnvironmentVariables environmentVariables
    = new EnvironmentVariables();

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

完全披露:我是这两个库的作者。

在类似的情况下,我必须编写依赖于环境变量的测试用例,我尝试了以下方法:

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");
    }
}
  • 我选择了Stefan Birkner建议的系统规则。它的用途很简单。但迟早,我发现这种行为不稳定。在一次运行中,它工作,在下一次运行中失败我调查发现,系统规则在JUnit 4或更高版本上运行良好。但在我的案例中,我使用了一些依赖于JUnit 3的JAR。所以我跳过了系统规则。更多关于它你可以在这里找到
  • 接下来,我尝试通过Java提供的processbuilder类创建环境变量。在这里,通过Java代码,我们可以创建一个环境变量,但是您需要知道进程程序的名称,而我没有知道。它还为子进程而不是主进程创建环境变量
  • 我用上述两种方法浪费了一天时间,但都没有用。然后玛文来救我。我们可以通过Maven POM文件设置环境变量系统属性,我认为这是对基于Maven的项目进行单元测试的最佳方法。下面是我在POM文件中的输入

        <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>
    
    
    org.apache.maven.plugins
    maven surefire插件
    属性值1
    财产价值2
    环境变量值1
    环境变量值2
    

    在这一更改之后,我再次运行了测试用例,突然所有测试用例都按预期工作。为了便于读者参考,我在Maven 3.x中探讨了这种方法,因此我不知道Maven 2.x我认为还没有提到这一点,但您也可以使用Powermockito

    public class TestClass {
        private static final Map<String, String> DEFAULTS = new HashMap<>(System.getenv());
        private static Map<String, String> envMap;
    
        @Test
        public void aTest() {
            assertEquals("6", System.getenv("NUMBER_OF_PROCESSORS"));
            System.getenv().put("NUMBER_OF_PROCESSORS", "155");
            assertEquals("155", System.getenv("NUMBER_OF_PROCESSORS"));
        }
    
        @Test
        public void anotherTest() {
            assertEquals("6", System.getenv("NUMBER_OF_PROCESSORS"));
            System.getenv().put("NUMBER_OF_PROCESSORS", "77");
            assertEquals("77", System.getenv("NUMBER_OF_PROCESSORS"));
        }
    
        /*
         * Restore default variables for each test
         */
        @BeforeEach
        public void initEnvMap() {
            envMap.clear();
            envMap.putAll(DEFAULTS);
        }
    
        @BeforeAll
        public static void accessFields() throws Exception {
            envMap = new HashMap<>();
            Class<?> clazz = Class.forName("java.lang.ProcessEnvironment");
            Field theCaseInsensitiveEnvironmentField = clazz.getDeclaredField("theCaseInsensitiveEnvironment");
            Field theUnmodifiableEnvironmentField = clazz.getDeclaredField("theUnmodifiableEnvironment");
            removeStaticFinalAndSetValue(theCaseInsensitiveEnvironmentField, envMap);
            removeStaticFinalAndSetValue(theUnmodifiableEnvironmentField, envMap);
        }
    
        private static void removeStaticFinalAndSetValue(Field field, Object value) throws Exception {
            field.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            field.set(null, value);
        }
    }
    
    鉴于:

    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");
        }
    }
    
    我使用System.getEnv()获取地图,并将其保留为字段,以便模拟它:

    public class AAA {
    
        Map<String, String> environmentVars; 
    
        public String readEnvironmentVar(String varName) {
            if (environmentVars==null) environmentVars = System.getenv();   
            return environmentVars.get(varName);
        }
    }
    
    
    
    public class AAATest {
    
             @Test
             public void test() {
                  aaa.environmentVars = new HashMap<String,String>();
                  aaa.environmentVars.put("NAME", "value");
                  assertEquals("value",aaa.readEnvironmentVar("NAME"));
             }
    }
    
    公共级AAA{
    地图环境变量;
    公共字符串readEnvironmentVar(字符串varName){
    如果(environmentVars==null)environmentVars=System.getenv();
    返回environmentVars.get(varName);
    }
    }
    公共A级考试{
    @试验
    公开无效测试(){
    aaa.environmentVars=新HashMap();
    aaa.环境变量put(“名称”、“值”);
    资产质量(“价值”,aaa.readEnvironmentVar(“名称”);
    }
    }
    
    希望问题得到解决。我只是想告诉你我的解决办法

    Map<String, String> env = System.getenv();
        new MockUp<System>() {
            @Mock           
            public String getenv(String name) 
            {
                if (name.equalsIgnoreCase( "OUR_OWN_VARIABLE" )) {
                    return "true";
                }
                return env.get(name);
            }
        };
    
    Map env=System.getenv();
    新模型(){
    @嘲弄
    公共字符串getenv(字符串名称)
    {
    if(name.equalsIgnoreCase(“我们自己的变量”)){
    返回“真”;
    }
    返回env.get(名称);
    }
    };
    
    尽管我认为它最适合Maven项目,但也可以通过reflect实现(在Java 8中测试):

    公共类TestClass{
    私有静态最终映射默认值=新HashMap(System.getenv());
    私有静态映射;
    @试验
    公共无效测试(){
    assertEquals(“6”,System.getenv(“处理器的数量”);
    System.getenv().put(“处理器的数量”,“155”);
    assertEquals(“155”,System.getenv(“数量
    
    PowerMockito.mockStatic(System.class);
    PowerMockito.when(System.getenv("MyEnvVariable")).thenReturn("DesiredValue");
    
    PowerMockito.mockStatic(System.class);
    PowerMockito.when(System.getenv(Mockito.anyString())).thenReturn(envVariable);
    
     <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.7.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @EnabledIfEnvironmentVariable(named = "MAVEN_CMD_LINE_ARGS", matches = "(.*)integration-testing(.*)")
    public @interface IntegrationTest {}
    
    mvn <cmds> -DmySystemPop=mySystemPropValue
    
    <!-- for JUnit 4 -->
    <dependency>
        <groupId>uk.org.webcompere</groupId>
        <artifactId>system-stubs-junit4</artifactId>
        <version>1.1.0</version>
        <scope>test</scope>
    </dependency>
    
    <!-- for JUnit 5 -->
    <dependency>
        <groupId>uk.org.webcompere</groupId>
        <artifactId>system-stubs-jupiter</artifactId>
        <version>1.1.0</version>
        <scope>test</scope>
    </dependency>
    
    @Rule
    public EnvironmentVariablesRule environmentVariablesRule = new EnvironmentVariablesRule();
    
    @Test
    public void givenEnvironmentCanBeModified_whenSetEnvironment_thenItIsSet() {
        // mock that the system contains an  environment variable "ENV_VAR" having value "value1"
        environmentVariablesRule.set("ENV_VAR", "value1");
    
        assertThat(System.getenv("ENV_VAR")).isEqualTo("value1");
    }