JUnit Параметризованный тест с примером использования @Parameters

⚡ Умное резюме

Параметризованные тесты в JUnit Запустите один и тот же тестовый метод несколько раз с разными входными значениями, чтобы один метод охватывал множество сценариев. Аннотации @RunWith и @Parameters предоставляют набор данных для каждой итерации.

  • 🔘 Источник данных: Статический метод `@Parameters` возвращает коллекцию массивов, и каждый массив становится одной тестовой итерацией.
  • ☑️ Второе место: @RunWith(Parameterized.class) заменяет стандартный блок.JUnit4ClassRunner перестраивает класс один раз для каждой строки данных.
  • Конструктор: Один публичный конструктор хранит одну строку данных в полях экземпляра, которые проверяет тестовый метод.
  • 🧪 Реализованный пример: Пять строк входных данных определяют результат проверки суммы (sum()), а также JUnit Просмотреть отчеты. Работает 5/5 без сбоев.
  • 🇧🇷 JUnit 5: Использование аннотаций `@ParameterizedTest` с `@ValueSource`, `@CsvSource` или `@MethodSource` позволяет удалить как исполнитель тестов, так и конструктор.
  • 📌 Подводные камни: Запуск программы останавливается из-за нестатического метода с аннотацией @Parameters, наличия двух открытых конструкторов или отсутствия зависимости junit-jupiter-params.

JUnit Параметризованный тест с использованием аннотаций @RunWith и @Parameters

Что такое параметризованный тест в JUnit?

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

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

Рассмотрим простой тест, который суммирует различные числа. Код может выглядеть так:

JUnit Тестовый метод повторяет три вызова метода assertEquals для метода sum.

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

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

Шаги по созданию параметризованного JUnit Тест

Приведённый ниже код демонстрирует пример параметризованного теста. Он проверяет метод sum() класса Airthematic, именно так он пишется во всём примере проекта.

Шаг 1) Создайте класс. В этом примере мы будем вводить два числа, используя метод sum(int, int), который вернет сумму заданных чисел.

Класс Airthematic объявляет открытый метод sum, который складывает два целочисленных аргумента.

Шаг 2) Создайте параметризованный тестовый класс.

Заголовок тестового класса аннотирован аннотацией @RunWith(Parameterized.class) и содержит четыре приватных поля.

Code объяснение

  • Code Линия 11: Добавьте аннотацию `@RunWith(Parameterized.class)` к вашему тестовому классу.
  • Code Линия 13: Объявление переменной firstNumber как частной и ее типа int.
  • Code Линия 14: Объявляем переменную 'secondNumber' как приватную и указываем её тип как int.
  • Code Линия 15: Объявление переменной 'expectedResult' как приватной и с типом int.
  • Code Линия 16: Объявляем переменную 'airthematic' приватной и указываем тип как Airthematic.

@Бежать с(class_name.class): @Бежать с Аннотация используется для указания имени класса исполнителя. Если в качестве параметра не указан тип, среда выполнения выберет другой тип. ЗаблокироватьJUnit4КлассБегун по умолчанию.

Этот класс отвечает за запуск тестов с новым экземпляром теста. Он отвечает за вызов... JUnit Методы управления жизненным циклом, такие как настройка (связывание ресурсов) и завершение (освобождение ресурсов), описаны в [ссылка на соответствующий раздел]. JUnit испытательный стенд учебное пособие.

Для параметризации необходимо аннотировать класс с помощью @RunWith и передать требуемый .class для тестирования.

Шаг 3) Создайте конструктор, который будет хранить тестовые данные. Он будет хранить 3 переменные.

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

Шаг 4) Создайте статический метод, который генерирует и возвращает тестовые данные.

Статический метод ввода, аннотированный @Parameterized.Parameters, возвращает двумерный массив объектов.

Code Линия 32,33: Создание двумерного массива (с указанием входных параметров для сложения). Используя метод asList, мы преобразуем данные в тип List, поскольку возвращаемый тип метода input — Collection.

Code Линия 30: Посмотрите на график @ Параметры аннотация для создания набора входных данных для запуска нашего теста.

Статический метод, обозначенный аннотацией @Parameters, возвращает коллекцию, где каждая запись в коллекции будет представлять собой входные данные для одной итерации теста. Рассмотрим элемент {1,2,3}. Здесь:

  • firstNumber = 1
  • secondNumber = 2
  • expectedResult = 3

Здесь каждый элемент массива будет передаваться в конструктор по одному, поскольку класс создается несколько раз. Таким образом, пять массивов, объявленных в примере, приводят к следующим пяти запускам:

итерация первый номер второй номер ожидаемый результат Консольная линия
[0] 1 2 3 Сумма Numbers = : 3
[1] 11 22 33 Сумма Numbers = : 33
[2] 111 222 333 Сумма Numbers = : 333
[3] 10 9 19 Сумма Numbers = : 19
[4] 100 9 109 Сумма Numbers = : 109

Шаг 5) Полный код.

Полный листинг AirthematicTest с импортами, конструктором, методами @Parameters и @Test.

Code Объяснение:

  • Code Линия 25: Для настройки ресурсов (здесь Airthematic.class) используется аннотация @Before. Аннотация @Before используется для запуска перед каждым тестовым случаем. Она содержит предварительное условие теста.
  • Code Линия 36: Для создания теста используем аннотацию @Test.
  • Code Линия 39: Процесс создания утверждение проверить, соответствует ли наша сумма нашим ожиданиям.

Шаг 6) Создайте класс-исполнитель для запуска параметризованного теста:

Класс TestRunner передает класс AirthematicTest.class в JUnitCore.runClasses и вывод ошибок

Code Объяснение:

  • Code Линия 8: Объявление основного метода класса Test, который будет запускать наш код. JUnit тест.
  • Code Линия 9: Выполнение тестовых случаев с использованием JUnitCore.runClasses, которая принимает в качестве параметра имя тестового класса (в нашем примере мы используем AirthematicTest.class).
  • Code Линия 11: Обработка результата с помощью цикла for и вывод на экран сообщения об ошибке.
  • Code Линия 13: Распечатка успешного результата.

Выход:

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

Eclipse JUnit Просмотр отчетов. Выполнено 5 из 5 раз без ошибок и сбоев для параметризованного класса.

Результат отображается в консоли, где показано сложение двух чисел:

Eclipse вывод на консоль одного Сумма Numbers строка для каждой из пяти строк параметров

Параметризованные тесты в JUnit 5 с @ParameterizedTest

Приведенный выше пример написан для JUnit 4. JUnit В версии 5 (Jupiter) полностью отказались от модели запуска, поэтому аннотация `@RunWith(Parameterized.class)`, конструктор данных и поля экземпляра исчезли. JUnit Приведенный выше код (см. пункт 4) не устарел: он по-прежнему работает без изменений. JUnit Платформа использует старый движок. Однако новые тесты обычно пишутся с аннотацией `@ParameterizedTest`.

Требуются две зависимости: junit-jupiter-api для аннотаций к тестам и junit-jupiter-params для параметризованной поддержки. Без второго артефакта исходные аннотации вообще не будут разрешаться.

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class AirthematicTest {

    // one row per iteration, no constructor and no runner
    @ParameterizedTest(name = "{0} + {1} = {2}")
    @CsvSource({"1, 2, 3", "11, 22, 33", "111, 222, 333", "10, 9, 19", "100, 9, 109"})
    void sumOfTwoNumbers(int firstNumber, int secondNumber, int expectedResult) {
        assertEquals(expectedResult, new Airthematic().sum(firstNumber, secondNumber));
    }
}

Jupiter предлагает несколько источников аргументации, и правильный из них зависит от формы данных:

Аннотация источника Дополнительно Используйте это, когда
@ValueSource Один столбец литералов Для проверки требуется ровно один аргумент.
@CsvSource Встроенные строки, разделенные запятыми Небольшие таблицы чисел и строк считываются в файле без проблем.
@CsvFileSource Строки, считанные из CSV-файла, находящегося в тестовом пути к классам. Набор данных большой или поддерживается вне кода.
@MethodSource Статическая фабрика, возвращающая поток аргументов. Необходимы реальные объекты, вычисленные значения или случайные данные.
@EnumSource Константы перечисления Каждое значение перечисления должно быть использовано.

Большинство новичков сталкиваются с двумя правилами. Аннотация `@Test`, размещенная на простом методе, молча игнорируется, поэтому метод должен содержать аннотацию `@ParameterizedTest`. И пустое значение без кавычек в `@CsvSource` читается как `null`, а пустое значение в кавычках — как пустая строка.

JUnit В этой статье используются 4 аннотации, которые отображаются в Jupiter следующим образом: @RunWith(Parameterized.class) становится @ParameterizedTest плюс аннотация источника, @Parameters становится @MethodSource или @CsvSource, а @Before становится @BeforeEach. Полный список приведен в [ссылка на статью]. JUnit аннотации учебное пособие.

Преимущества и ограничения параметризованных тестов

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

Преимущества

  • Less дублирование: Один из методов заменяет блок практически идентичных операторов assert, как показано на первом скриншоте в этой статье.
  • Более дешевое страховое покрытие: Добавление граничного случая требует добавления одной строки данных вместо создания совершенно новой строки. прецедент метод.
  • Точная отчетность: Каждая итерация отображается отдельно, поэтому... JUnit В представлении точно указывается, в какой именно строке произошел сбой, а не фиксируется один общий сбой.
  • Централизованные данные: Входные данные хранятся в одном методе и впоследствии могут быть перемещены в CSV-файл или фабрику без изменения утверждений.

Ограничения

  • Одна из форм утверждения: Для каждой строки выполняются одни и те же проверки, поэтому сценарий, требующий различных проверок, по-прежнему нуждается в собственном методе тестирования.
  • Область применения на уровне класса в JUnit 4: Исполнитель параметризует весь класс, поэтому несвязанные методы @Test в этом классе также выполняются один раз для каждой строки.
  • Нечитаемые отчеты: Без шаблона имени ошибки отображаются как testAirthematicTest[3], что ничего не говорит о данных, которые привели к ошибке.
  • Обширные встроенные данные: Большие массивы занимают место в логике тестирования; переместите их в фабрику @CsvFileSource или @MethodSource.

Распространенные ошибки в JUnit Параметризованные тесты

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

Сообщение Вызывать фиксированный
Тестовый класс должен иметь ровно один публичный конструктор. Класс не объявляет ни одного открытого конструктора, или объявляет два открытых конструктора. Используйте один публичный конструктор, параметры которого соответствуют столбцам данных.
В классе отсутствует открытый статический метод для работы с параметрами. Метод с аннотацией `@Parameters` не является `public static` или возвращает неверный тип. Объявите его как public static Collection и верните Arrays.asList(…)
IllegalArgumentException: неверное количество аргументов Строка может быть шире или уже, чем список параметров конструктора. Сделайте ширину каждого массива в коллекции равной ширине конструктора.
Ошибка конфигурации: отсутствуют аргументы поставщика. В тесте Jupiter используется аннотация `@ParameterizedTest` без аннотации `source`. Добавьте @ValueSource, @CsvSource, @CsvFileSource, @MethodSource или @EnumSource.
Аннотация источника, по всей видимости, ничего не делает. Метод аннотирован как @Test вместо @ParameterizedTest. Замените @Test на @ParameterizedTest и импортируйте junit-jupiter-params.

Ещё одна ловушка — это разделяемое состояние. Потому что JUnit Создается новый экземпляр для каждой строки, все, что хранится в статическом поле, сохраняется в каждой итерации, а значение, записанное в строке [0], может незаметно изменить результат строки [4]. Сохраняйте состояние каждой строки в полях экземпляра и сбрасывайте общие ресурсы в методе @Before или @BeforeEach. Общие рекомендации по изоляции тестов описаны в модульное тестирование учебное пособие.

Часто задаваемые вопросы (FAQ)

TestNG Передача данных осуществляется через метод с аннотацией `@DataProvider`, на который ссылается каждый тестовый метод, поэтому несвязанные тесты в классе остаются без изменений. JUnit 4 параметризует весь класс через его исполнитель. JUnit В версии 5 этот пробел устранен за счет использования аннотации @ParameterizedTest для каждого метода.

Да. JUnit В версии 4 принимается @Parameters(name = “{index}: sum({0},{1})={2}”), а в Jupiter — @ParameterizedTest(name = “…”). Заполнители заменяются во время выполнения, поэтому в отчете об ошибке указывается имя проблемной строки, а не отображается пустой индекс.

Да. JUnit 4 поддерживает @Parameter(0) и @Parameter(1) для открытых нестатических полей, и класс затем полагается на конструктор по умолчанию. Сочетание внедрения полей с конструктором данных вызывает ошибку "ровно один открытый конструктор".

JUnit Для версии 4 требуется только артефакт junit, поскольку параметризованный исполнитель поставляется внутри него. JUnit Для версии 5 необходимы пакеты junit-jupiter-params и junit-jupiter-api; без этого артефакта @ParameterizedTest все аннотации исходного кода не будут разрешаться.

Jupiter предоставляет аннотацию `@CsvFileSource(resources = “/data.csv”, numLinesToSkip = 1)`, которая считывает строки из тестового пути к классам. JUnit Встроенного аналога для параметра 4 нет, поэтому метод @Parameters должен открыть и проанализировать файл самостоятельно, прежде чем вернуть коллекцию.

In JUnit 4. Это возможно, но исполнитель параметризует весь класс, поэтому каждый метод выполняется один раз для каждой строки данных. Jupiter параметризует отдельные методы, поэтому обычные методы @Test в одном классе по-прежнему выполняются ровно один раз.

Искусственный интеллект-ассистенты считывают сигнатуру метода и предлагают граничные значения, такие как ноль, отрицательное значение, максимальное значение и значение переполнения, которые часто отсутствуют в таблице, составленной вручную. RevПросматривайте каждый сгенерированный ожидаемый результат, потому что модель может выдать правдоподобную строку с неверным ответом.

Второй пилот GitHub Быстро производит каркас, но часто смешивает компоненты. JUnit 4 и импортирует Jupiter, а иногда оставляет аннотацию исходного кода в простом методе @Test. Проверьте импорт перед запуском набора тестов.

Подведем итог этой публикации следующим образом: