Рубрики
Без рубрики

Руководство по Библиотеке системных правил

Подробно изучите библиотеку системных правил.

Автор оригинала: Jonathan Cook.

1. Обзор

Иногда при написании модульных тестов нам может потребоваться протестировать код, который напрямую взаимодействует с классом System . Обычно в таких приложениях, как инструменты командной строки, которые вызывают System.exit напрямую или читают аргументы с помощью System.in .

В этом уроке мы рассмотрим наиболее распространенные функции аккуратной внешней библиотеки под названием Системные правила , которая предоставляет набор правил JUnit для тестирования кода, использующего System класс .

2. Зависимости Maven

Во-первых, давайте добавим зависимость Системных правил в ваш pom.xml :


    com.github.stefanbirkner
    system-rules
    1.19.0

Мы также добавим Системную лямбда-зависимость , которая также доступна из Maven Central :


    com.github.stefanbirkner
    system-lambda
    1.1.0

Поскольку Системные правила напрямую не поддерживают JUnit 5 , мы добавили последнюю зависимость. Это предоставляет системные методы лямбда-оболочки для использования в тестах. Существует альтернатива на основе расширений ,называемая Системные заглушки .

3. Работа Со Свойствами Системы

Чтобы быстро резюмировать, платформа Java использует объект свойств для предоставления информации о локальной системе и конфигурации. Мы можем легко распечатать свойства:

System.getProperties()
  .forEach((key, value) -> System.out.println(key + ": " + value));

Как мы видим, свойства включают в себя такую информацию, как текущий пользователь, текущая версия среды выполнения Java и разделитель имен путей к файлам:

java.version: 1.8.0_221
file.separator: /
user.home: /Users/baeldung
os.name: Mac OS X
...

Мы также можем задать свои собственные системные свойства с помощью метода System.setProperty . При работе со свойствами системы из наших тестов следует соблюдать осторожность, так как эти свойства являются глобальными для JVM.

Например, если мы задаем системное свойство, мы должны убедиться, что восстановим его исходное значение после завершения теста или в случае сбоя. Иногда это может привести к громоздкой настройке и разрушению кода. Однако, если мы пренебрегаем этим, это может привести к неожиданным побочным эффектам в наших тестах.

В следующем разделе мы рассмотрим, как мы можем предоставлять, очищать и восстанавливать значения системных свойств после завершения наших тестов в сжатой и простой форме.

4. Предоставление Системных Свойств

Давайте представим, что у нас есть системное свойство log_dir , которое содержит местоположение, в которое должны записываться наши журналы, и наше приложение устанавливает это местоположение при запуске:

System.setProperty("log_dir", "/tmp/baeldung/logs");

4.1. Предоставить Единое свойство

Теперь давайте рассмотрим, что из нашего модульного теста мы хотим предоставить другое значение. Мы можем сделать это с помощью правила ProvideSystemProperty :

public class ProvidesSystemPropertyWithRuleUnitTest {

    @Rule
    public final ProvideSystemProperty providesSystemPropertyRule = new ProvideSystemProperty("log_dir", "test/resources");

    @Test
    public void givenProvideSystemProperty_whenGetLogDir_thenLogDirIsProvidedSuccessfully() {
        assertEquals("log_dir should be provided", "test/resources", System.getProperty("log_dir"));
    }
    // unit test definition continues
}

Используя правило ProvideSystemProperty , мы можем задать произвольное значение для данного системного свойства для использования в наших тестах. В этом примере мы устанавливаем свойство log_dir в наш каталог test/resources и из нашего модульного теста просто утверждаем, что значение свойства test было успешно предоставлено.

Если мы затем распечатаем значение свойства log_dir , когда наш тестовый класс завершится:

@AfterClass
public static void tearDownAfterClass() throws Exception {
    System.out.println(System.getProperty("log_dir"));
}

Мы видим, что стоимость имущества была восстановлена до его первоначальной стоимости:

/tmp/baeldung/logs

4.2. Предоставление Нескольких Свойств

Если нам нужно предоставить несколько свойств, мы можем использовать метод и для объединения в цепочку столько значений свойств, сколько потребуется для нашего теста:

@Rule
public final ProvideSystemProperty providesSystemPropertyRule = 
    new ProvideSystemProperty("log_dir", "test/resources").and("another_property", "another_value")

4.3. Предоставление Свойств Из Файла

Аналогично, у нас также есть возможность предоставить свойства из файла или ресурса пути к классу с помощью правила ProvideSystemProperty :

@Rule
public final ProvideSystemProperty providesSystemPropertyFromFileRule = 
  ProvideSystemProperty.fromResource("/test.properties");

@Test
public void givenProvideSystemPropertyFromFile_whenGetName_thenNameIsProvidedSuccessfully() {
    assertEquals("name should be provided", "baeldung", System.getProperty("name"));
    assertEquals("version should be provided", "1.0", System.getProperty("version"));
}

В приведенном выше примере мы предполагаем, что у нас есть файл test.properties в пути к классу:

name=baeldung
version=1.0

4.4. Предоставление Свойств С помощью JUnit 5 и Лямбд

Как мы уже упоминали ранее, мы также могли бы использовать системную лямбда-версию библиотеки для реализации тестов, совместимых с JUnit5.

Давайте посмотрим, как реализовать наш тест с помощью этой версии библиотеки:

@BeforeAll
static void setUpBeforeClass() throws Exception {
    System.setProperty("log_dir", "/tmp/baeldung/logs");
}

@Test
void givenSetSystemProperty_whenGetLogDir_thenLogDirIsProvidedSuccessfully() throws Exception {
    restoreSystemProperties(() -> {
        System.setProperty("log_dir", "test/resources");
        assertEquals("log_dir should be provided", "test/resources", System.getProperty("log_dir"));
    });

    assertEquals("log_dir should be provided", "/tmp/baeldung/logs", System.getProperty("log_dir"));
}

В этой версии мы можем использовать метод restoreSystemProperties для выполнения данного оператора. Внутри этого оператора мы можем настроить и предоставить значения, необходимые для наших системных свойств . Как мы видим, после завершения выполнения этого метода значение log_dir такое же, как и до /tmp/baeldung/logs .

К сожалению, нет встроенной поддержки для предоставления свойств из файлов с помощью метода restoreSystemProperties .

5. Свойства Клиринговой системы

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

Для этой цели мы можем использовать правило Clear System Properties :

@Rule
public final ClearSystemProperties userNameIsClearedRule = new ClearSystemProperties("user.name");

@Test
public void givenClearUsernameProperty_whenGetUserName_thenNull() {
    assertNull(System.getProperty("user.name"));
}

Системное свойство user.name – это одно из предопределенных системных свойств, которое содержит имя учетной записи пользователя. Как и ожидалось в приведенном выше модульном тесте, мы очищаем это свойство и проверяем, что оно пусто из нашего теста.

Удобно, что мы также можем передать несколько имен свойств в Очистить системные свойства конструктор.

6. Насмешка System.in

Время от времени мы можем создавать интерактивные приложения командной строки, которые читают из System.in .

Для этого раздела мы будем использовать очень простой пример, который считывает имя и фамилию из стандартного ввода и объединяет их вместе:

private String getFullname() {
    try (Scanner scanner = new Scanner(System.in)) {
        String firstName = scanner.next();
        String surname = scanner.next();
        return String.join(" ", firstName, surname);
    }
}

Системные правила содержат правило TextFromStandardInputStream , которое мы можем использовать для указания строк, которые должны быть предоставлены при вызове System.in :

@Rule
public final TextFromStandardInputStream systemInMock = emptyStandardInputStream();

@Test
public void givenTwoNames_whenSystemInMock_thenNamesJoinedTogether() {
    systemInMock.provideLines("Jonathan", "Cook");
    assertEquals("Names should be concatenated", "Jonathan Cook", getFullname());
}

Мы достигаем этого, используя предоставляет Строки метод, который принимает варарги параметр, позволяющий указать более одного значения.

В этом примере мы предоставляем два значения перед вызовом метода getFullName , где System.in является ссылкой. Наши два предоставленных значения строки будут возвращаться каждый раз, когда мы вызываем scanner.next() .

Давайте посмотрим, как мы можем добиться того же в версии теста JUnit 5 с использованием системной лямбды:

@Test
void givenTwoNames_whenSystemInMock_thenNamesJoinedTogether() throws Exception {
    withTextFromSystemIn("Jonathan", "Cook").execute(() -> {
        assertEquals("Names should be concatenated", "Jonathan Cook", getFullname());
    });
}

В этом варианте мы используем аналогично названные с Текстом Из Системы В метод, который позволяет нам указать предоставленный метод, который позволяет нам указать предоставленный ценности.

В обоих случаях важно отметить, что после завершения теста исходное значение System.in будет восстановлен.

7. Тестирование System.out и System.err

В предыдущем уроке мы рассмотрели, как использовать системные правила для модульного тестирования System.out.println() .

Удобно, что мы можем применить почти идентичный подход к тестированию кода, который взаимодействует со стандартным потоком ошибок. На этот раз мы используем правило System Err :

@Rule
public final SystemErrRule systemErrRule = new SystemErrRule().enableLog();

@Test
public void givenSystemErrRule_whenInvokePrintln_thenLogSuccess() {
    printError("An Error occurred Baeldung Readers!!");

    Assert.assertEquals("An Error occurred Baeldung Readers!!", 
      systemErrRule.getLog().trim());
}

private void printError(String output) {
    System.err.println(output);
}

Мило! Используя правило System Error , мы можем перехватывать записи в System.err . Во-первых, мы начинаем регистрировать все, что записано в System.error , вызывая метод enable Log в нашем правиле. Затем мы просто вызываем getLog , чтобы получить текст, записанный в System.err, так как мы вызвали EnableLog .

Теперь давайте реализуем версию нашего теста JUnit 5:

@Test
void givenTapSystemErr_whenInvokePrintln_thenOutputIsReturnedSuccessfully() throws Exception {

    String text = tapSystemErr(() -> {
        printError("An error occurred Baeldung Readers!!");
    });

    Assert.assertEquals("An error occurred Baeldung Readers!!", text.trim());
}

В этой версии мы используем метод tap System Err , который выполняет инструкцию и позволяет нам захватывать содержимое, переданное в System.err .

8. Система управления.выход

Приложения командной строки обычно завершаются вызовом System.exit . Если мы хотим протестировать такое приложение, вполне вероятно, что наш тест завершится ненормально до его завершения, когда он столкнется с кодом, вызывающим System.exit .

К счастью, Системные правила предоставляют простое решение для решения этой проблемы с помощью правила ExpectedSystemExit :

@Rule
public final ExpectedSystemExit exitRule = ExpectedSystemExit.none();

@Test
public void givenSystemExitRule_whenAppCallsSystemExit_thenExitRuleWorkssAsExpected() {
    exitRule.expectSystemExitWithStatus(1);
    exit();
}

private void exit() {
    System.exit(1);
}

Использование правила ExpectedSystemExit позволяет нам указать в нашем тесте ожидаемый вызов System.exit () . В этом простом примере мы также проверяем код ожидаемого состояния с помощью метода expect System Exit With Status .

Мы можем достичь чего-то подобного в нашей версии JUnit 5, используя метод catch SystemExit :

@Test
void givenCatchSystemExit_whenAppCallsSystemExit_thenStatusIsReturnedSuccessfully() throws Exception {
    int statusCode = catchSystemExit(() -> {
        exit();
    });
    assertEquals("status code should be 1:", 1, statusCode);
}

9. Заключение

Подводя итог, в этом уроке мы подробно изучили библиотеку системных правил.

Во-первых, мы начали с объяснения того, как тестировать код, использующий системные свойства. Затем мы рассмотрели, как протестировать стандартный вывод и стандартный ввод. Наконец, мы рассмотрели, как обрабатывать код, который вызывает System.exit из наших тестов.

Библиотека системных правил также обеспечивает поддержку предоставления переменных среды и специальных менеджеров безопасности из наших тестов . Обязательно ознакомьтесь с полной документацией для получения подробной информации.

Как всегда, полный исходный код статьи доступен на GitHub .