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

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

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



답변

라이브러리 시스템 규칙 은 환경 변수 설정을위한 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"));
  }
}

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


답변

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

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 클래스를 사용하여 환경 변수를 가져옵니다.


답변

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 에 대해서는 전혀 모릅니다 .


답변

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


답변

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


답변

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

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

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


답변

이 질문 에 대한 답변 은 Java에서 환경 변수를 어떻게 설정합니까? System.getenv ()에서 (수정 불가능한) 맵을 변경하는 방법을 제공합니다. 따라서 OS 환경 변수의 값을 실제로 변경하지는 않지만 System.getenv 가 반환 할 내용을 변경하므로 단위 테스트에 사용할 수 있습니다 .