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


139

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

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


이것은 테스트 용이므로 현재 시스템 규칙 단위 테스트 규칙이 가장 적합 할 수 있습니다.
Atifm

3
: 그냥 JUnit을 5 사용하는 동안 같은 질문에 관심이있는 사람들을 위해 stackoverflow.com/questions/46846503/...
펠리페 마틴 멜로

답변:


199

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

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

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


1
나는 이것을 @ClassRule로 사용하고 있는데, 사용 후에 재설정하거나 지우어야합니까? 그렇다면 어떻게해야합니까?
Mritunjay

필요하지 않습니다. 원래 환경 변수는 클래스의 모든 테스트가 실행 된 후 규칙에 의해 자동으로 재설정됩니다.
Stefan Birkner

이 방법은 JUnit 4 이상 버전에서만 작동합니다. JUnit 3 이하 버전 또는 JUnit 4와 JUnit 3을 혼합 한 경우 권장되지 않습니다.
RLD

2
import org.junit.contrib.java.lang.system.EnvironmentVariables;com.github.stefanbirkner:system-rules프로젝트에 종속성을 추가해야합니다 . MavenCentral에서 사용할 수 있습니다.
Jean Bob

2
종속성을 추가하는 지침은 다음과 같습니다. stefanbirkner.github.io/system-rules/download.html
Guilherme Garnier

77

일반적인 솔루션은이 환경 변수에 대한 액세스를 관리하는 클래스를 작성하여 테스트 클래스에서 모의 ​​할 수 있습니다.

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
나는이 질문이 오래되었다는 것을 알고 있지만 이것이 정답이라고 말하고 싶었습니다. 받아 들여진 대답은 시스템에 숨겨진 의존성으로 열악한 디자인을 장려하는 반면,이 대답은 시스템을 주입해야 할 또 다른 의존성으로 취급하는 적절한 디자인을 장려합니다.
앤드류

30

Environment Variable에 의존하는 Test Case 를 작성 해야하는 이와 비슷한 상황에서 다음을 시도했습니다.

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

위의 두 가지 접근법을 사용하여 하루를 낭비했지만 아무 소용이 없습니다. 그런 다음 Maven 이 구조했습니다. Maven 기반 프로젝트에 대한 단위 테스트 를 수행하는 가장 좋은 방법은 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 에 대해서는 전혀 모릅니다 .


2
이 솔루션은 가장 좋으며 lib와 같은 추가 항목이 필요하지 않기 때문에 허용되는 솔루션이어야합니다. Maven만으로도 충분히 편리합니다. 감사합니다 @RLD
Semo

@Semo 그것은 maven이 필요하지만 lib를 사용하는 것보다 훨씬 더 큰 요구 사항입니다. Junit Test를 pom에 연결하고 이제 IDE에서 일반적인 방식으로 직접 실행하는 대신 항상 mvn에서 테스트를 실행해야합니다.
Chirlo

@ Chilo, 그것은 당신이 당신의 프로그램을 묶고 싶은 것에 달려 있습니다. Maven을 사용하면 한곳에서 구성하고 깔끔하고 간결한 코드를 작성할 수 있습니다. 라이브러리를 사용하는 경우 여러 위치에서 코드를 작성해야합니다. JUnits 실행 시점과 관련하여 Maven을 사용하더라도 Eclipse와 같은 IDE에서 JUnits를 실행할 수 있습니다.
RLD

@RLD, Eclipse에서 내가 아는 유일한 방법은 Junit 대신 'Maven'실행 구성으로 실행하는 것입니다. 그리고 나는 깔끔하고 간결한 코드 포인트를 따르지 않고 여러 장소에서 코드를 작성해야합니다. 나를 위해, Junit 테스트에 사용되는 pom에 테스트 데이터를 갖는 것은 그것들을 함께 갖는 것보다 모호합니다. 나는 최근 에이 상황에 있었고 MathewFarwell의 접근법에 따라 끝났고 라이브러리 / pom 트릭이 필요하지 않으며 모든 것이 동일한 테스트에 함께 있습니다.
Chirlo

1
이로 인해 환경 변수가 하드 코딩되어 System.getenv의 한 호출에서 다음 호출로 변경할 수 없습니다. 옳은?
Ian Stewart

12

가장 깨끗한 방법은 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());
}

12

나는 이것이 아직 언급되지 않았다고 생각하지만 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");
    }
}

8
when(System.getenv("FOO_VAR_1")).thenReturn("test-foo-var-1")원인이 org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'.오류
Andremoniy

10

Java 변수를 환경 변수에서 분리하면 코드를 읽을 환경 코드를 EnvironmentVariableReader로 인식 할 수있는보다 추상적 인 변수 리더가 제공됩니다.

그런 다음 테스트에서 테스트 값을 제공하는 변수 판독기의 다른 구현을 제공 할 수 있습니다.

의존성 주입이 도움이 될 수 있습니다.



4

문제가 해결되기를 바랍니다. 방금 해결책을 말하려고 생각했습니다.

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

1
JMockit을 사용하고 있음을 언급하지 않았습니다. :) 어쨌든,이 솔루션은 또한 JUnit을 5 위대한 작품을
라이언 J. 맥도

1

이 답변 이 Maven 프로젝트에 가장 적합 하다고 생각하더라도 반영을 통해 달성 할 수 있습니다 ( Java 8 에서 테스트 ).

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

감사합니다! Java의 내 버전 에는 다음과 같은 theCaseInsensitiveEnvironment필드 가 없고 대신 필드가 있습니다 theEnvironment.```envMap = new HashMap <> (); 클래스 <?> clazz = Class.forName ( "java.lang.ProcessEnvironment"); Field theEnvironmentField = clazz.getDeclaredField ( "theEnvironment"); UnmodifiableEnvironmentField 필드 = clazz.getDeclaredField ( "theUnmodifiableEnvironment"); removeStaticFinalAndSetValue (EnvironmentField, envMap); removeStaticFinalAndSetValue (수정 불가능한 환경 필드, envMap); ```
Intenex

-2

setup () 메소드를 사용하여 env의 다른 값을 선언 할 수 있습니다. 상수의 변수. 그런 다음 다른 시나리오를 테스트하는 데 사용되는 테스트 방법에서이 상수를 사용하십시오.


-2

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

이 방법 getEnv()은 또한 인수를 취할 수 있습니다. 예를 들면 :

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

테스트를 위해 다음과 같은 작업을 수행합니다.

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

1
요점은 junit 테스트에서 env 변수에 액세스하지 않는 것입니다
Tanmoy Bhattacharjee

-2

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"));
         }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.