Написать пост

Параметризованные тесты в Java

Аватарка пользователя Aleksandr Kovalenko

Рассказали о библиотеке для запуска параметризованных тестов на Java. Они позволяют запустить один и тот же тест с разными входными данными.

Тестирование кода является важной частью работы каждого программиста. Порой до половины времени разработки новой фичи может уходить на написание Unit тестов (а иногда процент еще выше). По этой причине актуальным является вопрос о снижении трудозатрат на тестирование. К счастью разработчики одного из самых популярных тестовых Java фреймворков JUnit 5 разработали библиотеку, позволяющую создавать параметризованные тесты. Они позволяют запустить один и тот же тест с различными входными данными.

Это особенно полезно, когда требуется проверить одну и ту же функциональность с разными наборами параметров. Однако, данная функциональность не входит в базовую библиотеку JUnit 5. Для того, чтобы она стала доступна, нужно включить библиотеку junit-jupiter-params в ваш проект. Если в качестве сборщика проекта вы используете Maven, то включите следующий код в ваш pom.xml:

			<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-params</artifactId>
  <version>5.10.0</version>
  <scope>test</scope>
</dependency>
		

Если же вы используете Gradle, то для добавления нужной библиотеки достаточно одной новой строки в вашем build.gradle файле:

			testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0'
		

Перейдем к тестированию

Тестировать будем класс, проверяющий, является ли объект enum’a java.time.Month нечетным месяцем.

			import java.time.Month;

public class OddMonthDetector {
    public static boolean isOdd(Month month) {
        return month.getValue() % 2 == 1;
    }
}
		

Рассматриваемая библиотека junit-jupiter-params предоставляет различные способы передачи параметров в тесты.

Передача примитивов или строк

			@ParameterizedTest
@ValueSource(strings = {"JANUARY", "MARCH", "MAY", "NOVEMBER"})
void isOddTest(String monthName) {
  Month month = Month.valueOf(monthName);
  boolean actualResult = OddMonthDetector.isOdd(month);
  Assertions.assertTrue(actualResult);
}
		

В приведенном выше коде используется аннотация @ParameterizedTest, которая позволяет запустить тест несколько раз с разными значениями.

Аннотация @ValueSource указывает, что тест будет запущен с набором строк, перечисленных в массиве.

В данном случае, тест isOddTest будет запущен четыре раза с параметрами “JANUARY”, “MARCH”, “MAY” и “NOVEMBER”, представляющими названия месяцев.

Помимо строк можно использовать любые примитивы, например int:

			@ParameterizedTest
@ValueSource(ints = {1, 3, 5})
void isOddTest(int monthNumber) {
  Month month = Month.of(monthNumber);
  boolean actualResult = OddMonthDetector.isOdd(month);
  Assertions.assertTrue(actualResult);
}
		

В этом примере используется аннотация @ValueSource с целочисленными значениями. Тест isOddTest будет запущен три раза с параметрами 1, 3 и 5, представляющими номера месяцев.

Передача инстансов enum

			@ParameterizedTest
@EnumSource(value = Month.class, names = {"JANUARY", "MARCH", "MAY", "NOVEMBER"})
void isOddTest(Month month) {
  boolean actualResult = OddMonthDetector.isOdd(month);
  Assertions.assertTrue(actualResult);
}
		

В этом примере используется аннотация @EnumSource, которая позволяет передавать перечисления в качестве параметров теста.

Тест isOddTestбудет запущен четыре раза с объектами enum’а Month.class: JANUARY, MARCH, MAY и NOVEMBER.

Другие способы

Помимо предыдущих примеров, также имеется возможность работать с параметризованными тестами, используя датасеты в формате CSV. Это позволяет эффективно тестировать разнообразные сценарии, в которых в тест требуется передавать более одного параметра.

			@ParameterizedTest
@CsvSource({
        "JANUARY,true",
        "FEBRUARY,false",
        "MARCH,true",
        "OCTOBER,false",
        "NOVEMBER,true"
})
void isOddCsvSourceTest(String monthName, boolean expectedResult) {
    Month month = Month.valueOf(monthName);
    boolean actualResult = OddMonthDetector.isOdd(month);
    Assertions.assertEquals(expectedResult, actualResult);
}
		

В этом примере используется аннотация @CsvSource, позволяющая передавать данные в формате CSV. В каждой строке CSV файла указывается название месяца и ожидаемый результат. Тест isOddCsvSourceTest будет запущен с каждой парой значений. Количество параметров может быть любым, не обязательно равным двум, как в примере.

Той же функциональности можно достичь, читая датасет из CSV-файла:

Параметризованные тесты в Java 1
Датасет из CSV-файла
			@ParameterizedTest
@CsvFileSource(resources = "oddMonthTestDataset.csv", delimiter = ';')
void isOddCsvFileSourceTest(String monthName, boolean expectedResult) {
  Month month = Month.valueOf(monthName);
  boolean actualResult = OddMonthDetector.isOdd(month);
  Assertions.assertEquals(expectedResult, actualResult);
}
		

В этом примере используется аннотация @CsvFileSource, которая позволяет читать данные из файла oddMonthTestDataset.csv. Также имеется возможность задать разделитель полей с помощью параметра delimiter (по умолчанию delimiter – это запятая).

Помимо прочего, библиотека junit-jupiter-params с помощью аннотации @MethodSource предоставляет удобный способпередачи в тестовый метод параметров, создаваемых программным кодом с помощью статического метода.

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

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

Количество параметров также не ограничивается двумя (как в примере), вы можете передавать их любое количество.

			@ParameterizedTest
@MethodSource("provideOddMonthTestArguments")
void isOddMethodSourceTest(Month month, boolean expectedResult) {
    boolean actualResult = OddMonthDetector.isOdd(month);
    Assertions.assertEquals(expectedResult, actualResult);

}
private static Stream<Arguments> provideOddMonthTestArguments() {
    return Stream.of(
            Arguments.of(Month.JANUARY, true),
            Arguments.of(Month.FEBRUARY, false),
            Arguments.of(Month.MARCH, true),
            Arguments.of(Month.OCTOBER, false),
            Arguments.of(Month.NOVEMBER, true)
    );
}
		

Заключение

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

Следите за новыми постами
Следите за новыми постами по любимым темам
689 открытий2К показов