java - 如何在maven中测试之前取消设置环境变量

标签 java maven unit-testing testing

我有一些依赖于环境变量的单元测试。

我想在使用 maven 进行测试之前取消设置这些环境变量。

问题:我怎样才能做到这一点?

最佳答案

不幸的是,在这种情况下您不能使用 environmentVariables Maven Surefire Plugin 的选项,主要是因为它只会添加新的环境变量,但不会覆盖(或重置,实际上等于覆盖为空值)现有变量。

另请注意:ant run包裹在 Maven 中并在测试前执行也不起作用。

建议的解决方案基于 Java,尤其是 this approach在另一个 SO 帖子中提出。虽然不建议用于应用程序代码,但这种hack 对于测试代码来说可能是可以接受的。

您可以将以下类添加到您的测试代码中(在 src/test/java 下):

package com.sample;

import java.lang.reflect.Field;
import java.util.Map;

public class EnvHack {

    @SuppressWarnings("unchecked")
    public static void resetEnvironmentVariable(String name, String value) throws Exception {
        Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
        Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
        theEnvironmentField.setAccessible(true);
        Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
        env.put(name, value);
        Field theCaseInsensitiveEnvironmentField = processEnvironmentClass
                .getDeclaredField("theCaseInsensitiveEnvironment");
        theCaseInsensitiveEnvironmentField.setAccessible(true);
        Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
        cienv.put(name, value);
    }

    public static void main(String[] args) throws Exception {
        resetEnvironmentVariable("test_prop", "test_value");
    }
}

上面的类基本上是破解 Java API 以更改内存中环境变量的值。因此,它们可以设置为不同的值、重置甚至取消设置(将其从 map 中删除)。

由于该类现在是您测试代码的一部分,您有多种选择:

  • 在某个 JUnit 测试用例的 @Before 方法(或 @BeforeClass, with a certain difference )中使用该类(在相关类的每个 JUnit 方法之前)
  • 在 JUnit 测试方法中使用它(自定义和缩小用法)
  • 在执行任何 JUnit 测试之前运行其主要方法(以更全局的方式),如下所述(并且可能回答问题,即使其他场景也值得一提,恕我直言)。

让我们看看每种可能的解决方案。

在某个JUnit测试用例的@Before方法中使用类

package com.sample;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class MainTest {

    @Before
    public void init() throws Exception {
        EnvHack.resetEnvironmentVariable("test_prop", "test_value");
    }

    @Test
    public void testEnvProperty() throws Exception {
        String s = System.getenv("test_prop");
        Assert.assertEquals(s, "test_value");
    }

}

此解决方案可用于每个测试类,并且当一组测试(方法)共享相同的要求时(建议:如果不这样做,可能是一个提示,可能应该移出某些方法)。

在 JUnit 测试方法中使用它

package com.sample;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class MainTest {

    @Before
    public void init() throws Exception {
        EnvHack.resetEnvironmentVariable("test_prop", "test_value");
    }

    @Test
    public void testEnvProperty() throws Exception {
        EnvHack.resetEnvironmentVariable("test_prop", "test_value2");
        String s = System.getenv("test_prop");
        Assert.assertEquals(s, "test_value2");
    }

}

此解决方案提供了最高的自由度:您可以在需要的地方准确地使用相关变量,尽管可能会出现代码重复,但它也可以强制执行测试隔离。

在任何执行的 JUnit 测试之前运行它的 main 方法

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.4.0</version>
    <executions>
        <execution>
            <phase>process-test-classes</phase>
            <goals>
                <goal>java</goal>
            </goals>
            <configuration>
                <mainClass>com.sample.EnvHack</mainClass>
                <classpathScope>test</classpathScope>
            </configuration>
        </execution>
    </executions>
</plugin>

请注意我们在这种情况下所做的事情:

  • 我们正在调用 java Exec Maven Plugin 的目标实际调用我们的 env hack 类的 mainClass
  • 我们将 classPathScope 设置为 test 以使其对 Enforcer 插件可见
  • 我们将其作为 process-test-classes 的一部分运行阶段只是为了确保它在 test 阶段之前执行,因此在任何测试之前执行。

此解决方案集中了整个准备环境过程,一次用于所有测试。


另一方面,您也可以考虑在测试中使用模拟。这不是一个集中式选项(除非您对其进行编码)但可以提供进一步的提示,因此值得一提。这是通过 PowerMock 重置环境变量的示例代码

package com.sample;

import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(System.class)
public class EnvTest {

    @Before
    public void init() {
        PowerMockito.mockStatic(System.class);
        Mockito.when(System.getenv("TEST_PROP")).thenReturn("TEST_VALUE");
    }

    @Test
    public void test() {
        String var = System.getenv("TEST_PROP");
        Assert.assertEquals("TEST_VALUE", var);
    }
}

这类似于上面提出的第一种和第二种方法。

请注意,要使其正常工作,您需要将以下依赖项添加到您的 POM 中:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.5</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-core</artifactId>
        <version>1.5</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>1.5</version>
        <scope>test</scope>
    </dependency>
</dependencies>

一般说明:确实不建议进行未完全隔离且依赖于环境变量存在(或不存在)的测试。对于同事或您自己的 future ,您可能会遇到维护问题或进行更棘手的故障排除。因此,如果您确实需要它,最好将其正确记录下来。

关于java - 如何在maven中测试之前取消设置环境变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36192164/

相关文章:

unit-testing - 是否有 Kotest 断言来测试列表是否包含具有给定属性的元素?

java - 如何防止 Eclipse 进入 Java 库代码

java - Eclipse 无法打开并显示 "an error has occurred"?

java - 用 while 循环结束我的游戏 -- java

Maven 错误 : InvocationTargetException: Tomcat connector in failed state fiware-cepheus

unit-testing - ScalaTest:使用 ShouldMatcher 检查序列的内容

java - 如何使用 Spring JMS 支持在运行时创建 JMS 队列?

java - 导入的maven项目尝试使用jre系统库

java - EnableTransactionManagement 无法解析为类型

android - 仪器测试 : define a test as mandatory for the following