Это продолжение туториала по JUnit 5. Введение опубликовано здесь.
Оглавление
Настройка
Аннотация @ParameterizedTest
Источники тестовых аргументов
Параметризованные тесты с несколькими аргументами.
Вывод
1. Настройка
Включите зависимость junit-jupiter-params
, чтобы использовать параметризованные тесты.
Последнюю версию можно найти по этой ссылке.
pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit-version}</version>
<scope>test</scope>
</dependency>
2. Аннотация @ParameterizedTest
Используйте аннотацию @ParameterizedTest, чтобы выполнить тест несколько раз, но с разными аргументами. Нам не нужно использовать аннотацию @Test, вместо этого в таких тестах используется только аннотация @ParameterizedTest.
Мы должны объявить по крайней мере один источник аргументов, предоставляющий аргументы для каждого вызова, которые будут использоваться в тестовом методе.
В данном примере
testPalindrome
будет вызываться 2 раза для каждой строки, указанной в аннотации@ValueSource
. Мы получаем доступ к аргументу, используя параметрword
метода.Используйте аргумент
name
в аннотации @ParameterizedTest, чтобы настроить отображаемое сообщение.
public class ParameterizedTests
{
public boolean isPalindrome(String s) {
return s == null ? false : StringUtils.reverse(s).equals(s);
}
@ParameterizedTest(name = "{index} - {0} is a palindrome")
@ValueSource(strings = { "12321", "pop" })
void testPalindrome(String word) {
assertTrue(isPalindrome(word));
}
}
3. Источники тестовых аргументов
Есть несколько способов передать аргументы методу тестирования. Давайте изучим их.
3.1. Аннотация @ValueSource
Используйте
@ValueSource
для простых буквальных значений, таких как примитивы и строки.Она определяет один массив значений и может использоваться только для предоставления одного аргумента для каждого параметризованного вызова теста.
Java поддерживает автобоксирование, поэтому мы также можем использовать литералы в их классах-оболочках.
Мы не можем передавать
null
в качестве аргумента даже для типов String и Class.
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testMethod(int argument) {
//test code
}
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testMethodWithAutoboxing(Integer argument) {
//test code
}
3.2. Аннотация @NullSource
Она предоставляет единственный null
аргумент методу, аннотированному @ParameterizedTest.
@ParameterizedTest
@NullSource
void testMethodNullSource(Integer argument) {
assertTrue(argument == null);
}
3.3. Аннотация @EmptySource
Она предоставляет метод, аннотированный @ParameterizedTest, с единственным пустым аргументом следующих типов:
java.lang.String
java.util.List
java.util.Set
java.util.Map
примитивные массивы (например, int [])
массивы объектов (например, String [])
@ParameterizedTest
@EmptySource
void testMethodEmptySource(String argument) {
assertTrue(StringUtils.isEmpty(argument));
}
3.4. Аннотация @NullAndEmptySource
Она сочетает в себе функциональность @NullSource и @EmptySource. В данном примере тестовый метод будет вызываться два раза - сначала со значением null
, а затем со значением empty
.
@ParameterizedTest
@NullAndEmptySource
void testMethodNullAndEmptySource(String argument) {
assertTrue(StringUtils.isEmpty(argument));
}
Проверка null и non-null значений в одном тесте
Мы уже знаем, что аннотация @ValueSource не поддерживает значение
null
.Таким образом, используя
@NullSource
и@EmptySource
в аннотации@ValueSource
, мы можем тестировать null, non-null и пустые значения в одном и том же тесте.
3.5. Аннотация @EnumSource
Это удобный способ использования Enum
констант. Метод тестирования будет вызываться для каждой константы перечисления за раз.
В данном примере тестовый метод будет вызываться 4 раза, по одному разу для каждой Enum
константы.
enum Direction {
EAST, WEST, NORTH, SOUTH
}
@ParameterizedTest
@EnumSource(Direction.class)
void testWithEnumSource(Direction d) {
assertNotNull(d);
}
3.6. Аннотация @MethodSource
Она используется для ссылки на один или несколько фабричных методов тестового класса или внешних классов. Фабричный метод должен генерировать поток аргументов, где каждый аргумент в потоке будет использоваться методом, аннотированным @ParameterizedTest.
Фабричный метод должен быть
static
, если тестовый класс не аннотирован с помощью@TestInstance(Lifecycle.PER_CLASS)
.Кроме того, фабричный метод не должен принимать аргументы.
@ParameterizedTest
@MethodSource("argsProviderFactory")
void testWithMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> argsProviderFactory() {
return Stream.of("alex", "brian");
}
Если мы явно не предоставим имя фабричного метода через @MethodSource, JUnit будет искать фабричный метод, имя которого по умолчанию совпадает с именем текущего метода с аннотацией @ParameterizedTest
.
Поэтому, в примере, если мы не предоставим имя метода argsProviderFactory
в аннотации @MethodSource
, Junit будет искать имя метода testWithMethodSource
с возвращаемым типом Stream<String>
.
@ParameterizedTest
@MethodSource
void testWithMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> testWithMethodSource() {
return Stream.of("alex", "brian");
}
Также поддерживаются потоки для примитивных типов (DoubleStream, IntStream и LongStream).
@ParameterizedTest
@MethodSource("argsProviderFactory")
void testWithMethodSource(int argument) {
assertNotEquals(9, argument);
}
static IntStream argsProviderFactory() {
return IntStream.range(0, 10);
}
3.7. Аннотация @CsvSource
Эта аннотация позволяет нам задавать списки аргументов как значения, разделенные запятыми. Каждый CSV токен представляет собой строку CSV и приводит к одному вызову параметризованного теста.
Задайте для свойства ignoreLeadingAndTrailingWhitespace
значение true или false, указывающее на то, что Junit должен принимать или игнорировать пробелы в CSV токенах.
@ParameterizedTest
@CsvSource(value = {
"alex, 30",
"brian, 35",
"charles, 40"
}, ignoreLeadingAndTrailingWhitespace = true)
void testWithCsvSource(String name, int age) {
assertNotNull(name);
assertTrue(age > 0);
}
3.8. Аннотация @CsvFileSource
Эта аннотация очень похожа на @CsvSource
за исключением того, что мы читаем токены CSV из файла вместо чтения токенов в исходном тексте. CSV файл можно прочитать по classpath или из локальной файловой системы.
Разделителем по умолчанию является запятая (,
), но мы можем использовать другой символ, установив атрибут разделителя.
Обратите внимание, что любая строка, начинающаяся с символа #
, будет интерпретироваться как комментарий и игнорироваться.
@ParameterizedTest
@CsvFileSource(resources = "employeeData.csv", numLinesToSkip = 0)
void testWithCsvFileSource(String name, int age) {
assertNotNull(name);
assertTrue(age > 0);
}
3.9. Аннотация @ArgumentsSource
Аннотацию @ArgumentsSource можно использовать для указания настраиваемого многоразового поставщика аргументов ArgumentsProvider.
@ParameterizedTest(name = "{index} - {0} is older than 40")
@ArgumentsSource(EmployeesArgumentsProvider.class)
void isEmployeeAgeGreaterThan40(Employee e) {
assertTrue(Period.between(e.getDob(), LocalDate.now()).get(ChronoUnit.YEARS) > 40);
}
class EmployeesArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of(new Employee(1, "Alex", LocalDate.of(1980, 2, 3))),
Arguments.of(new Employee(2, "Brian", LocalDate.of(1979, 2, 3))),
Arguments.of(new Employee(3, "Charles", LocalDate.of(1978, 2, 3)))
);
}
}
4. Параметризованные тесты с несколькими аргументами.
Чтобы написать тесты, которые могут использовать несколько аргументов, мы можем использовать следующие аннотации:
4.1. Аннотация @CsvSource
Как показано в предыдущем разделе 3.7, с помощью аннотации @CsvSource
мы можем предоставить множество литералов и простых типов аргументов.
Нам нужно предоставить все аргументы в одном токене CSV, а затем определить соответствующие аргументы метода.
@ParameterizedTest
@CsvSource({
"alex, 30, HR, Active",
"brian, 35, Technology, Active",
"charles, 40, Finance, Purged"
})
void testWithCsvSource(String name, int age, String department, String status) {
//test code
}
4.2. Интерфейс ArgumentsProvider
Чтобы предоставить несколько тестовых аргументов сложных или настраиваемых типов, мы должны использовать аннотацию @ArgumentsSource
с аннотацией ArgumentsProvider
.
В примере мы передаем три аргумента метода тестирования testArgumentsSource
, типов Employee
, LocalDate
и enum константы типа Direction
.
@ParameterizedTest
@ArgumentsSource(EmployeesArgumentsProvider.class)
void testArgumentsSource(Employee e, LocalDate date, Direction d) {
assertTrue(Period.between(e.getDob(), LocalDate.now()).get(ChronoUnit.YEARS) > 40);
assertNotNull(date);
assertNotNull(d);
}
class EmployeesArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of(new Employee(1, "Alex",
LocalDate.of(1980, 2, 3)), LocalDate.now(), Direction.EAST),
Arguments.of(new Employee(2, "Brian",
LocalDate.of(1979, 2, 3)), LocalDate.now(), Direction.NORTH),
Arguments.of(new Employee(3, "Charles",
LocalDate.of(1978, 2, 3)), LocalDate.now(), Direction.SOUTH)
);
}
5. Вывод
Junit 5 аннотация @ParameterizedTest очень полезна при написании тестов, которые необходимо вызывать для тестирования несколько раз, но с разными аргументами. Junit предоставляет несколько способов, которые можно использовать декларативно для предоставления аргументов методу тестирования.
Мы также можем комбинировать разные аннотации для тестирования различных типов входных данных в одном тесте.
Есть некоторые более сложные понятия, такие как агрегатор аргументов, конвертер аргументов и т.д. Пожалуйста, прочитайте официальную документацию Junit для получения последней информации.
Хорошего изучения!!!