it-swarm.com.ru

Как проверить код, зависящий от переменных среды, используя JUnit?

У меня есть кусок кода Java, который использует переменную среды, и поведение кода зависит от значения этой переменной. Я хотел бы протестировать этот код с различными значениями переменной среды. Как я могу сделать это в JUnit?

Я видел некоторые способы установки переменных среды в Java в целом, но меня больше интересует аспект модульного тестирования, особенно учитывая, что тесты не должны мешать друг другу.

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

Отказ от ответственности: я автор Системных Правил.

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

Затем тестируемый класс получает переменную среды, используя класс Environment, а не напрямую из System.getenv ().

61
Matthew Farwell

В подобной ситуации, как это, где я должен был написать контрольный пример , который зависит от переменной среды , я попробовал следующее:

  1. Я выбрал Системные правила , как предложил Стефан Биркнер . Его использование было простым. Но рано или поздно я обнаружил, что поведение странное. В одном запуске это работает, в следующем запуске это терпит неудачу. Я исследовал и обнаружил, что системные правила хорошо работают с JUnit 4 или более поздней версией. Но в моих случаях я использовал несколько Jar-файлов, которые зависели от JUnit 3 . Поэтому я пропустил Системные правила . Подробнее об этом вы можете узнать здесь аннотация @ Rule не работает при использовании TestSuite в JUnit .
  2. Затем я попытался создать переменную среды через класс Process Builder , предоставленный Java . Здесь посредством Java кода мы можем создать переменную окружения, но вам нужно знать процесс или название программы которого я не сделал. Также он создает переменную среды для дочернего процесса, а не для основного процесса.

Я потратил впустую день, используя два вышеупомянутых подхода, но безрезультатно. Тогда Maven пришел ко мне на помощь. Мы можем установить переменные среды или системные свойства через Файл 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. х .

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

Отсоедините код Java от переменной Environment, предоставляя более абстрактное средство чтения переменных, которое вы реализуете с EnvironmentVariableReader, из которого код тестирует данные для чтения.

Затем в вашем тесте вы можете дать другую реализацию считывателя переменных, которая предоставляет ваши тестовые значения.

В этом может помочь инъекция зависимости.

7
Andrea Colleoni

Этот ответ на вопрос Как мне установить переменные окружения из Java? предоставляет способ изменить (немодифицируемую) карту в System.getenv (). Поэтому, хотя он НЕ ДЕЙСТВИТЕЛЬНО изменяет значение переменной среды ОС, он может использоваться для модульного тестирования, поскольку он изменяет то, что 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);
        }
    };
2
Muthu