web-dev-qa-db-ja.com

JUnitを使用して環境変数に依存するコードをテストする方法は?

環境変数を使用するJavaコードの一部があり、コードの動作はこの変数の値に依存します。環境変数の異なる値でこのコードをテストしたいと思います。 JUnitでこれを行うにはどうすればよいですか?

私は Javaで環境変数を設定するいくつかの方法 を見てきましたが、特にテストが互いに干渉してはならないことを考慮して、ユニットテストの側面に興味があります。

103
vitaut

ライブラリ システムルール は、環境変数を設定するための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"));
  }
}

免責事項:私はシステムルールの著者です。

144
Stefan Birkner

通常の解決策は、この環境変数へのアクセスを管理するクラスを作成し、テストクラスでモックすることです。

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クラスを使用して環境変数を取得します。

62
Matthew Farwell

環境変数に依存するテストケースを書かなければならなかったこのような同様の状況で、私は次のことを試しました:

  1. Stefan Birknerが示唆するように、システムルールに行きました。その使用は簡単でした。しかし、遅かれ早かれ、私はその振る舞いが不安定になることを発見した。ある実行では機能し、次の実行では失敗します。 調査し、システムルールがJUnit 4以降のバージョンでうまく機能することを発見しました。しかし、私の場合、 JUnit 3。そこで、 System Rules をスキップしました。詳細については、こちらを参照してください JUnitでTestSuiteを使用している間は@Ruleアノテーションは機能しません
  2. 次に、 Java が提供する Process Builder クラスを介して Environment Variable を作成しようとしました。ここではJavaコードを使用して環境変数を作成できますが、 process または program の名前を知っている必要があります私はしませんでした。また、メインプロセスではなく、子プロセスの環境変数を作成します。

上記の2つのアプローチを使用して1日無駄にしましたが、役に立ちませんでした。それから Maven が私の助けになりました。 Environment Variables または System Properties から Maven POM ファイルを設定できます単体テスト Maven ベースのプロジェクトを実行します。以下は、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 についてはわかりません。

14
RLD

これについてはまだ言及されていないと思いますが、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");
    }
}
11
Mangusta

Javaコードを環境変数から分離して、読み取りをテストするコードをEnvironmentVariableReaderで実現するより抽象的な変数リーダーを提供します。

次に、テストで、テスト値を提供する変数リーダーの異なる実装を提供できます。

依存性注入はこれに役立ちます。

7
Andrea Colleoni

この回答 質問に対する Javaから環境変数を設定するにはどうすればよいですか? は、System.getenv()の(変更不可能な)Mapを変更する方法を提供します。したがって、OS環境変数の値を実際に変更するわけではありませんが、 System.getenv が返すものを変更するため、単体テストに使用できます。

7
sudocode

これを行う最もクリーンな方法は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());
}
5
pgkelley

問題が解決されることを願っています。私は自分の解決策を伝えると思った。

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