Перейти к основному содержимому

Введение в JUnitParams

· 6 мин. чтения

1. Обзор

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

Бывают ситуации, когда единственное, что меняется между несколькими тестами, — это параметры. Сам JUnit имеет поддержку параметризации, и JUnitParams значительно улучшает эту функциональность.

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

Чтобы использовать JUnitParams в нашем проекте, нам нужно добавить его в наш pom.xml :

<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.0</version>
</dependency>

Последнюю версию библиотеки можно найти здесь .

3. Тестовый сценарий

Давайте создадим класс, который выполняет безопасное сложение двух целых чисел. Это должно возвращать Integer.MAX_VALUE , если оно переполняется, и Integer.MIN_VALUE , если оно недополняется:

public class SafeAdditionUtil {

public int safeAdd(int a, int b) {
long result = ((long) a) + b;
if (result > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else if (result < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
return (int) result;
}
}

4. Создание простого метода тестирования

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

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

@RunWith(JUnitParamsRunner.class)
public class SafeAdditionUtilTest {

private SafeAdditionUtil serviceUnderTest
= new SafeAdditionUtil();

@Test
@Parameters({
"1, 2, 3",
"-10, 30, 20",
"15, -5, 10",
"-5, -10, -15" })
public void whenWithAnnotationProvidedParams_thenSafeAdd(
int a, int b, int expectedValue) {

assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}

}

Теперь давайте посмотрим, чем этот тестовый класс отличается от обычного тестового класса JUnit .

Первое, что мы замечаем, это то, что в аннотации класса есть другой тест-раннер — JUnitParamsRunner .

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

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

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.foreach.junitparams.SafeAdditionUtilTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.068 sec
- in com.foreach.junitparams.SafeAdditionUtilTest

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

5. Различные типы параметризации методов испытаний

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

  • Непосредственно в аннотации @Parameters (используется в примере выше)
  • Использование именованного метода тестирования, определенного в аннотации
  • Использование метода, отображаемого по имени метода тестирования
  • Именованный тестовый класс, определенный в аннотации
  • Использование CSV-файла

Давайте рассмотрим подходы один за другим.

5.1. Непосредственно в аннотации @Parameters

Мы уже использовали этот подход в примере, который мы пробовали. Что нам нужно иметь в виду, так это то, что мы должны предоставить массив строк параметров. В строке параметров каждый параметр отделяется запятой.

Например, массив будет иметь форму {"1, 2, 3", "-10, 30, 20"}, а один набор параметров будет представлен как "1, 2, 3" .

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

5.2. Параметр Метод

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

@Test
@Parameters(method = "parametersToTestAdd")
public void whenWithNamedMethod_thenSafeAdd(
int a, int b, int expectedValue) {

assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}

private Object[] parametersToTestAdd() {
return new Object[] {
new Object[] { 1, 2, 3 },
new Object[] { -10, 30, 20 },
new Object[] { Integer.MAX_VALUE, 2, Integer.MAX_VALUE },
new Object[] { Integer.MIN_VALUE, -8, Integer.MIN_VALUE }
};
}

Тестовый метод аннотирован относительно метода parametersToAdd() и извлекает параметры, запуская указанный метод.

В результате спецификация метода провайдера должна возвращать массив Object . Если метод с заданным именем недоступен, тестовый пример завершается с ошибкой:

java.lang.RuntimeException: Could not find method: bogusMethodName so no params were used.

5.3. Метод сопоставлен по имени метода тестирования

Если мы ничего не указываем в аннотации @Parameters, JUnitParams пытается загрузить метод поставщика тестовых данных на основе имени тестового метода. Имя метода строится как «parametersFor»+ <имя метода тестирования>:

@Test
@Parameters
public void whenWithnoParam_thenLoadByNameSafeAdd(
int a, int b, int expectedValue) {

assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}

private Object[] parametersForWhenWithnoParam_thenLoadByNameSafe() {
return new Object[] {
new Object[] { 1, 2, 3 },
new Object[] { -10, 30, 20 },
new Object[] { Integer.MAX_VALUE, 2, Integer.MAX_VALUE },
new Object[] { Integer.MIN_VALUE, -8, Integer.MIN_VALUE }
};
}

В приведенном выше примере тестовый метод называется whenWithnoParam_shouldLoadByNameAbdSafeAdd() .

Поэтому, когда тестовый метод выполняется, он ищет метод поставщика данных с именем parametersForWhenWithnoParam_shouldLoadByNameAbdSafeAdd() .

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

5.4. Именованный тестовый класс, определенный в аннотации

Подобно тому, как мы ссылались на метод поставщика данных в предыдущем примере, мы можем обратиться к отдельному классу, чтобы предоставить данные для нашего теста:

@Test
@Parameters(source = TestDataProvider.class)
public void whenWithNamedClass_thenSafeAdd(
int a, int b, int expectedValue) {

assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}
public class TestDataProvider {

public static Object[] provideBasicData() {
return new Object[] {
new Object[] { 1, 2, 3 },
new Object[] { -10, 30, 20 },
new Object[] { 15, -5, 10 },
new Object[] { -5, -10, -15 }
};
}

public static Object[] provideEdgeCaseData() {
return new Object[] {
new Object[] {
Integer.MAX_VALUE, 2, Integer.MAX_VALUE },
new Object[] {
Integer.MIN_VALUE, -2, Integer.MIN_VALUE },
};
}
}

У нас может быть любое количество поставщиков тестовых данных в классе, если имя метода начинается с «предоставить». Если это так, исполнитель выбирает эти методы и возвращает данные.

Если никакие методы класса не удовлетворяют этому требованию, даже если эти методы возвращают массив Object s, эти методы будут игнорироваться.

5.5. Использование CSV-файла

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

Допустим, у нас есть CSV-файл с тестовыми параметрами в виде JunitParamsTestParameters.csv :

1,2,3
-10, 30, 20
15, -5, 10
-5, -10, -15

Теперь давайте посмотрим, как этот файл можно использовать для загрузки тестовых параметров в тестовом методе:

@Test
@FileParameters("src/test/resources/JunitParamsTestParameters.csv")
public void whenWithCsvFile_thenSafeAdd(
int a, int b, int expectedValue) {

assertEquals(expectedValue, serviceUnderTest.safeAdd(a, b));
}

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

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

В этом руководстве мы рассмотрели, как мы можем использовать функциональные возможности JUnitParams в двух словах.

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

Как всегда, исходный код можно найти на GitHub .