有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!


共 (1) 个答案

  1. # 1 楼答案

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

    还要注意:用Maven包装并在测试之前执行的ant run也不起作用

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

    您可以将以下类添加到测试代码中(在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进行黑客攻击,以更改内存中环境变量的值。因此,它们可以设置为不同的值,重置,甚至取消设置(从映射中删除)

    由于该类现在是测试代码的一部分,您有几个选项:

    • 在某个JUnit测试用例的@Before方法(或@BeforeClasswith a certain difference)中使用该类(在相关类的每个JUnit方法之前)
    • 在JUnit测试方法中使用它(自定义和缩小使用范围)
    • 在执行任何JUnit测试之前运行它的主方法(以更全局的方式),如下所述(可能会回答这个问题,尽管其他场景也值得一提,imho)

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

    在某个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测试之前运行其主方法

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

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

    • 我们正在调用Exec Maven Plugin^{}目标来实际调用我们的环境黑客类的mainClass
    • 我们调用它时classPathScope设置为test,以使它对Enforcer插件可见
    • 我们将它作为^{}阶段的一部分运行,只是为了确保它在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>
    

    一般注意事项:确实,不建议进行未完全隔离的测试,并取决于环境变量的存在(或不存在)。您可能会遇到维护问题,或者遇到更棘手的故障排除,无论是对同事还是对您的未来都是如此。所以,如果你真的需要它,最好把它记录下来