Programing

JUnit을 사용하여 환경 변수에 의존하는 코드를 테스트하는 방법은 무엇입니까?

lottogame 2020. 8. 13. 07:40
반응형

JUnit을 사용하여 환경 변수에 의존하는 코드를 테스트하는 방법은 무엇입니까?


환경 변수를 사용하는 Java 코드가 있으며 코드의 동작은이 변수의 값에 따라 다릅니다. 환경 변수의 다른 값으로이 코드를 테스트하고 싶습니다. JUnit에서 어떻게 할 수 있습니까?

일반적으로 Java 에서 환경 변수를 설정하는 몇 가지 방법을 보았지만 특히 테스트가 서로 간섭해서는 안된다는 점을 고려할 때 단위 테스트 측면에 더 관심이 있습니다.


라이브러리 시스템 규칙 은 환경 변수 설정을위한 JUnit 규칙을 제공합니다.

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

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

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

면책 조항 : 저는 시스템 규칙의 저자입니다.


일반적인 해결책은이 환경 변수에 대한 액세스를 관리하는 클래스를 만드는 것입니다. 그런 다음 테스트 클래스에서 모의 ​​작업을 수행 할 수 있습니다.

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 ()에서 직접이 아닌 Environment 클래스를 사용하여 환경 변수를 가져옵니다.


환경 변수에 종속 된 테스트 케이스 를 작성 해야하는 이와 비슷한 상황에서 다음을 시도했습니다.

  1. 나는 Stefan Birkner가 제안한 시스템 규칙 을 선택했습니다 . 사용은 간단했습니다. 그러나 얼마 지나지 않아 나는 행동이 이상하다는 것을 알았습니다. 한 번의 실행에서는 작동하지만 다음 실행에서는 실패합니다. 조사 결과 시스템 규칙이 JUnit 4 이상 버전에서 잘 작동하는 것으로 나타났습니다. 하지만 제 경우에는 JUnit 3에 의존하는 일부 Jar를 사용하고있었습니다 . 그래서 시스템 규칙을 건너 뛰었습니다 . 자세한 내용 은 JUnit에서 TestSuite를 사용하는 동안 @Rule 주석이 작동하지 않습니다 .
  2. 다음 으로 Java에서 제공하는 Process Builder 클래스를 통해 환경 변수 를 만들어 보았습니다 . 여기서 Java 코드를 통해 환경 변수를 만들 수 있지만 내가 몰랐던 프로세스 또는 프로그램 이름 을 알아야합니다 . 또한 메인 프로세스가 아닌 자식 프로세스에 대한 환경 변수를 생성합니다.

위의 두 가지 접근 방식을 사용하여 하루를 낭비했지만 아무 소용이 없습니다. 그런 다음 Maven 이 나를 구했습니다. 우리는 설정할 수있는 환경 변수 또는 시스템 등록 정보 를 통해 메이븐 POM의 내가 할 수있는 최선의 방법은 생각 파일 단위 테스트를 위해 메이븐 기반 프로젝트를. 아래는 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>

이 변경 후 테스트 케이스를 다시 실행 했고 갑자기 모두 예상대로 작동했습니다. 독자의 정보를 위해 Maven 3.x 에서이 접근 방식을 탐색 했으므로 Maven 2.x 에 대해 전혀 모릅니다 .


아직 언급되지 않은 것 같지만 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");
    }
}

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.


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.


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

Hope the issue is resolved. I just thought to tell my solution.

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

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.


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

I use System.getEnv() to get the map and I keep as a field, so I can mock it:

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

참고URL : https://stackoverflow.com/questions/8168884/how-to-test-code-dependent-on-environment-variables-using-junit

반응형