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

Ввод параметров в JUnit юпитера единицы испытаний

Быстрый взгляд на то, как расширить JUnit 5 с параметрами.

Автор оригинала: J Steven Perry.

1. Обзор

До JUnit 5, чтобы ввести новую классную функцию, команда JUnit должна была сделать это с основным API. С JUnit 5 команда решила, что пришло время расширить возможности для расширения основного API JUnit за пределы самого JUnit, основной философии JUnit 5 под названием « предпочитают точки расширения по “.

В этой статье мы сосредоточимся на одном из этих интерфейсов точки расширения – ПараметрРесольвер – что вы можете использовать для введения параметров в ваши методы тестирования. Есть несколько различных способов, чтобы сделать платформу JUnit известно о вашем продлении (процесс, известный как “регистрация”), и в этой статье, мы сосредоточимся на декларативные регистрации (т.е. регистрации через исходный код).

2. ПараметрРесольвер

Ввод параметров в методы тестирования может быть выполнен с помощью API JUnit 4, но он был довольно ограничен. С JUnit 5 API юпитера может быть расширен – путем внедрения ПараметрРесольвер – для обслуживания объектов любого типа для ваших методов тестирования. Давайте посмотрим.

2.1. FooParameterResolver

public class FooParameterResolver implements ParameterResolver {
  @Override
  public boolean supportsParameter(ParameterContext parameterContext, 
    ExtensionContext extensionContext) throws ParameterResolutionException {
      return parameterContext.getParameter().getType() == Foo.class;
  }

  @Override
  public Object resolveParameter(ParameterContext parameterContext, 
    ExtensionContext extensionContext) throws ParameterResolutionException {
      return new Foo();
  }
}

Во-первых, мы должны ПараметрРесольвер – который имеет два метода:

  • поддерживаетПараметр () – возвращает верно, если тип параметра поддерживается (Foo в этом примере), и
  • решитьПараматер () – служит объект правильного типа (новый экземпляр Foo в этом примере), который затем будет введен в ваш метод тестирования

2.2. FooTest

@ExtendWith(FooParameterResolver.class)
public class FooTest {
    @Test
    public void testIt(Foo fooInstance) {
        // TEST CODE GOES HERE
    }  
}

Затем, чтобы использовать расширение, мы должны объявить его – т.е. рассказать о нем платформе JUnit – через @ExtendWith аннотация (линия 1).

Когда платформа JUnit запускает ваш унитарный тест, она получит Фу например, из FooParameterРесольвер и передать его в testIt () метод (линия 4).

Расширение имеет сфера влияния , который активирует расширение, в зависимости от где это объявлено.

Расширение может быть активным в:

  • уровень метода, где он активен только для этого метода, или
  • уровень класса, где он активен для всего тестового класса, или @Nested тестовый класс, как мы скоро увидим

Примечание: вы не должны объявлять ПараметрРесольвер в обоих сферах для одного и того же параметра типа , или платформа JUnit будет жаловаться на эту двусмысленность .

Для этой статьи мы увидим, как писать и использовать два расширения для введения Лицо объекты: тот, который вводит “хорошие” данные ( ValidPersonParameterResolver ) и тот, который вводит “плохие” данные ( ИнвалидПераметрРесольвер ). Мы будем использовать эти данные для унитарного тестирования класса под названием PersonValidator , что подтверждает состояние Лицо объект.

3. Напишите расширения

Теперь, когда мы понимаем, что ПараметрРесольвер расширение, мы готовы написать:

  • тот, который обеспечивает действительный Лицо объекты ( ValidPersonParameterResolver ), и
  • тот, который обеспечивает недействительные Лицо объекты ( ИнвалидПераметрРесольвер )

3.1. ValidPersonParameterResolver

public class ValidPersonParameterResolver implements ParameterResolver {

  public static Person[] VALID_PERSONS = {
      new Person().setId(1L).setLastName("Adams").setFirstName("Jill"),
      new Person().setId(2L).setLastName("Baker").setFirstName("James"),
      new Person().setId(3L).setLastName("Carter").setFirstName("Samanta"),
      new Person().setId(4L).setLastName("Daniels").setFirstName("Joseph"),
      new Person().setId(5L).setLastName("English").setFirstName("Jane"),
      new Person().setId(6L).setLastName("Fontana").setFirstName("Enrique"),
  };

Обратите внимание на VALID_PERSONS массив Лицо Объектов. Это хранилище действительных Лицо объекты, из которых один будет выбран случайным образом каждый раз, когда решитьПараметр () метод называется платформой JUnit.

Наличие действительных объектов Person здесь выполняет две вещи:

  1. Разделение проблем между унитарным тестом и данными, которые его движет
  2. Повторное использование, если другие унитарные тесты требуют действительного Лицо объекты, чтобы управлять ими
@Override
public boolean supportsParameter(ParameterContext parameterContext, 
  ExtensionContext extensionContext) throws ParameterResolutionException {
    boolean ret = false;
    if (parameterContext.getParameter().getType() == Person.class) {
        ret = true;
    }
    return ret;
}

Если тип параметра Лицо , затем расширение говорит платформе JUnit, что он поддерживает этот тип параметра, в противном случае он возвращается ложным, говоря, что это не так.

Почему это должно иметь значение? Хотя примеры в этой статье просты, в реальном приложении классы единицы тестирования могут быть очень большими и сложными, со многими методами тестирования, которые принимают различные типы параметров. Платформа JUnit должна проверить все зарегистрированные ПараметрРесольвер s при разрешении параметров в рамках нынешнего сферы влияния .

@Override
public Object resolveParameter(ParameterContext parameterContext, 
  ExtensionContext extensionContext) throws ParameterResolutionException {
    Object ret = null;
    if (parameterContext.getParameter().getType() == Person.class) {
        ret = VALID_PERSONS[new Random().nextInt(VALID_PERSONS.length)];
    }
    return ret;
}

Случайная Лицо объект возвращается из VALID_PERSONS массив. Обратите внимание, как решитьПараметр () называется платформой JUnit только в том случае, если поддерживаетПараметр () возвращает истинное .

3.2. ИнвалидПерсонПераметрРесольвер

public class InvalidPersonParameterResolver implements ParameterResolver {
  public static Person[] INVALID_PERSONS = {
      new Person().setId(1L).setLastName("Ad_ams").setFirstName("Jill,"),
      new Person().setId(2L).setLastName(",Baker").setFirstName(""),
      new Person().setId(3L).setLastName(null).setFirstName(null),
      new Person().setId(4L).setLastName("Daniel&").setFirstName("{Joseph}"),
      new Person().setId(5L).setLastName("").setFirstName("English, Jane"),
      new Person()/*.setId(6L).setLastName("Fontana").setFirstName("Enrique")*/,
  };

Обратите внимание на INVALID_PERSONS массив Лицо Объектов. Так же, как с ValidPersonParameterResolver этот класс содержит храним «плохие» (т.е. недействительные) данные для использования в унитарных тестах для обеспечения, например, PersonValidator.ПроверкаИсключения должным образом брошены в присутствии недействительных данных:

@Override
public Object resolveParameter(ParameterContext parameterContext, 
  ExtensionContext extensionContext) throws ParameterResolutionException {
    Object ret = null;
    if (parameterContext.getParameter().getType() == Person.class) {
        ret = INVALID_PERSONS[new Random().nextInt(INVALID_PERSONS.length)];
    }
    return ret;
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, 
  ExtensionContext extensionContext) throws ParameterResolutionException {
    boolean ret = false;
    if (parameterContext.getParameter().getType() == Person.class) {
        ret = true;
    }
    return ret;
}

Остальная часть этого класса, естественно, ведет себя точно так же, как его “хороший” коллега.

4. Объявить и использовать расширения

Теперь, когда у нас есть ПараметрРесольвер s, пришло время использовать их. Давайте создадим тестовый класс JUnit для PersonValidator называется PersonValidatorТест .

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

  • @ DisplayName – это имя, которое появляется на тестовых отчетах, и гораздо более читаемым человеком
  • @ Вложенные – создает вложенный тестовый класс, в комплекте со своим собственным жизненным циклом тестирования, отдельно от родительского класса
  • @ Повторныйпротест – тест повторяется количество раз, указанное атрибутом значения (10 в каждом примере)

С помощью q Вложенные классы, мы можем тестировать как действительные, так и недействительные данные в одном и том же тестовом классе, сохраняя при этом их полностью песочницей друг от друга:

@DisplayName("Testing PersonValidator")
public class PersonValidatorTest {

    @Nested
    @DisplayName("When using Valid data")
    @ExtendWith(ValidPersonParameterResolver.class)
    public class ValidData {
        
        @RepeatedTest(value = 10)
        @DisplayName("All first names are valid")
        public void validateFirstName(Person person) {
            try {
                assertTrue(PersonValidator.validateFirstName(person));
            } catch (PersonValidator.ValidationException e) {
                fail("Exception not expected: " + e.getLocalizedMessage());
            }
        }
    }

    @Nested
    @DisplayName("When using Invalid data")
    @ExtendWith(InvalidPersonParameterResolver.class)
    public class InvalidData {

        @RepeatedTest(value = 10)
        @DisplayName("All first names are invalid")
        public void validateFirstName(Person person) {
            assertThrows(
              PersonValidator.ValidationException.class, 
              () -> PersonValidator.validateFirstName(person));
        }
    }
}

Обратите внимание, как мы можем использовать ValidPersonParameterResolver и ИнвалидПераметрРесольвер расширения в рамках одного и того же основного тестового класса , объявив их только в Вложенные уровень класса. Попробуйте это с JUnit 4! (Спойлер оповещения: вы не можете это сделать!)

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

В этой статье мы изучили, как написать два ПараметрРесольвер расширения – для обслуживания действительных и недействительных объектов. Затем мы посмотрим, как использовать эти два ПараметрРесольвер реализации в единице теста.

Как всегда, код доступен более на Github .

И, если вы хотите узнать больше о модели расширения JUnit Jupiter, ознакомьтесь с Руководство пользователя JUnit 5 , или часть 2 моего учебника по developerWorks .