1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/liushide-junit5_cn_doc

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
junit5UserGuide_zh_cn.md 110 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 30.11.2024 04:03 a26a331

JUnit 5: руководство пользователя (китайская версия)

Версия 5.0.3

JUnit 5 = Платформа 1.0.3 + Юпитер 5.0.3 + Винтаж 4.12.3

Руководство пользователя JUnit 5 Chinese document было написано на основе английской версии JUnit 5 User Guide с добавлением некоторых собственных интерпретаций, которые могут помочь вам лучше понять материал. Если вы считаете, что это полезно для вас, пожалуйста, поставьте «star».

Обратите внимание, что синтаксис Markdown может быть несовместим в некоторых случаях с git, поэтому рекомендуется скачать PDF-документ [junit5UserGuide_zh_cn.pdf] для просмотра.

Новые функции JUnit5 включают в себя хорошую поддержку Java8, возможность добавления параметров в тестовые методы, поглощение некоторых функций TestNG, дружественную поддержку сторонних тестовых библиотек и сохранение обратной совместимости при больших изменениях (можно использовать JUnit5 и JUnit4 одновременно).

Благодарим авторов английской версии Stefan Bechtold, Sam Brannen, Johannes Link, Matthias Merdes, Marc Philipp, Christian Stein за их труд.

Авторские права © 2017–2018, liushide 刘士德 (liushide@qq.com)

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

Если у вас есть вопросы, отправьте электронное письмо мне (liushide@qq.com), или напрямую создайте issue.

TOC

  1. Обзор

Цель этого документа — предоставить исчерпывающее руководство для разработчиков тестов, разработчиков расширений (extension authors), разработчиков движков (engine authors), а также поставщиков инструментов сборки и IDE.

Скачать PDF-версию [junit5UserGuide_zh_cn.pdf].

1.1. Что такое JUnit 5?

В отличие от предыдущих версий JUnit, JUnit 5 состоит из трёх различных модулей:

  • JUnit Platform (основа платформы);
  • JUnit Jupiter (Юпитер, ядро программы);
  • JUnit Vintage (поддержка старых версий).

JUnit Platform — это основа для запуска тестовых фреймворков на JVM. Он также определяет API для тестового движка, который используется для разработки платформ, на которых выполняются тестовые фреймворки (TestEngine). Кроме того, платформа предоставляет консольный лаунчер, который можно запустить из командной строки для запуска платформы, а также для Gradle и Maven. Также существует основанный на JUnit 4 раннер, который позволяет запускать любой TestEngine на платформе.

JUnit Jupiter — это новая модель программирования для написания тестов и расширений в JUnit 5 (Programming model и Extension model). Кроме того, проект Jupiter также предоставляет TestEngine, который запускает тесты на основе Jupiter на платформе.

JUnit Vintage предоставляет TestEngine, который позволяет выполнять JUnit 3 и JUnit 4 на платформе.

1.2. Поддержка Java-версии

Для работы JUnit 5 требуется Java 8 или более поздняя версия. Однако вы всё ещё можете тестировать код, скомпилированный с использованием старой версии JDK.

1.3. Получение помощи

Задавайте вопросы о JUnit 5 на Stack Overflow или присоединяйтесь к обсуждению на Gitter.

  1. Установка

Окончательные и веховые версии развёртываются в центральных репозиториях Maven.

Версии снимков развёртываются в хранилище снимков Sonatype (в /org/junit).

2.1. Зависимости

2.1.1 JUnit Platform

Group ID: org.junit.platform Version: 1.0.3 Artifact IDs:

  • junit-platform-commons — внутренние библиотеки и утилиты JUnit. Эти утилиты предназначены только для использования внутри самой платформы JUnit и не предназначены для внешнего использования. Они не несут ответственности за риски, связанные с внешним использованием!
  • junit-platform-console — поддерживает обнаружение и выполнение тестов на консоли JUnit Platform. Подробнее см. в разделе «Консольный лаунчер».
  • junit-platform-console-standalone — автономный исполняемый JAR-файл, содержащий все зависимости, необходимые для junit-platform-console. Подробнее см. в разделе «Консольный лаунчер».
  • junit-platform-engine — общий API для тестирования двигателей. См. раздел «Подключение собственного тестового двигателя».
  • junit-platform-gradle-plugin — поддержка обнаружения и выполнения тестов на JUnit Platform с помощью Gradle.
  • junit-platform-launcher — общий API конфигурации и запуска тестовых планов, обычно используемый IDE и инструментами сборки. Подробнее см. в разделе JUnit Platform Launcher API.
  • junit-platform-runner — Runner для выполнения тестов и наборов тестов на JUnit Platform в среде JUnit 4. Подробнее см. в разделе Использование JUnit 4 для запуска JUnit Platform.
  • junit-platform-suite-api — аннотации для настройки тестовых наборов на JUnit Platform, поддерживаемые junit-platform-runner и реализуемые сторонними TestEngine.
  • junit-platform-surefire-provider — поддержка обнаружения и выполнения тестов на JUnit Platform с помощью Maven Surefire.

2.1.2 JUnit Jupiter

Group ID: org.junit.jupiter Version: 5.0.3 Artifact IDs:

  • junit-jupiter-api — API для написания тестов и расширения в JUnit Jupiter.
  • junit-jupiter-engine — реализация тестового механизма JUnit Jupiter, необходимая только во время выполнения.
  • junit-jupiter-params — поддержка параметризованных тестов в JUnit Jupiter.
  • junit-jupiter-migrationsupport — поддержка миграции с JUnit 4 на JUnit Jupiter, необходима только в тестах, использующих правила JUnit 4. 2.1.3 JUnit Vintage (поддержка старых версий)

Group ID: org.junit.vintage

Version: 4.12.3

Artifact ID: junit-vintage-engine

JUnit Vintage — это реализация тестового движка, позволяющая запускать старые тесты JUnit, то есть тесты, написанные в стиле JUnit 3 или JUnit 4, на новой платформе JUnit.

2.1.4 Необязательные зависимости

Все вышеперечисленное имеет необязательную зависимость, которая находится в опубликованном Maven POM в следующих JAR-файлах @API Guardian.

Group ID: org.apiguardian

Artifact ID: apiguardian-api

Version: 1.0.0

Кроме того, большинство из вышеперечисленного напрямую или косвенно зависит от следующего JAR OpenTest4J.

Group ID: org.opentest4j

Artifact ID: opentest4j

Version: 1.0.0

2.2 Диаграмма зависимостей

2.3 Образцы проектов JUnit Jupiter

Библиотека образцов JUnit5 (junit5-samples) содержит набор примеров проектов, основанных на JUnit Jupiter и JUnit Vintage. Вы можете найти файлы сборки build.gradle и pom.xml для Gradle и Maven в следующих проектах:

— Для Gradle см. проект junit5 - Gradle - consumer.

— Для Maven см. проект junit5 -maven- consumer.

3. Написание тестов

Первый тестовый пример

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

import org.junit.jupiter.api.Test;

class FirstJUnit5Tests {

    @Test
    void myFirstTest() {
        assertEquals(2, 1 + 1);
    }

}

3.1 Описание аннотаций

В JUnit Jupiter используются следующие аннотации для настройки тестов и расширения фреймворка. Все основные аннотации находятся в пакете org.junit.jupiter.api модуля junit-jupiter-api.

Аннотация Описание
@Test Указывает, что метод является тестовым методом. В отличие от аннотации @Test в JUnit 4, эта аннотация не объявляет никаких свойств, поскольку расширение JUnit Jupiter основано на собственных специализированных аннотациях. Эти методы могут быть унаследованы, если они не переопределены.
@ParameterizedTest Указывает, что метод представляет собой параметризованный тест. Эти методы также могут быть унаследованы, если они не переопределены.
@RepeatedTest Указывает, что метод является шаблоном повторяющегося теста. Эти методы также могут быть унаследованы, если они не переопределены.
@TestFactory Указывает, что метод используется для создания динамических тестов. Эти методы также могут быть унаследованы, если они не переопределены.
@TestInstance Используется для настройки жизненного цикла тестовых экземпляров для классов с аннотациями. Эти аннотации могут быть унаследованы.
@TestTemplate Указывает, что метод служит шаблоном для тестовых случаев, которые вызываются несколько раз на основе зарегистрированных провайдеров. Эти методы также могут быть унаследованы, если они не переопределены.
@DisplayName Объявляет настраиваемое отображаемое имя для класса тестирования или метода тестирования. Такие аннотации не могут быть унаследованы.
@BeforeEach Указывает, что этот метод должен выполняться перед каждым методом @Test, @RepeatedTest, @ParameterizedTest или @TestFactory в текущем классе. Этот метод аналогичен методу @Before в JUnit 4. Эти методы также могут быть унаследованы, если они не переопределены.
@AfterEach Указывает, что этот метод должен выполняться после каждого метода @Test, @RepeatedTest, @ParameterizedTest или @TestFactory в текущем классе. Этот метод аналогичен методу @After в JUnit 4. Эти методы также могут быть унаследованы, если они не переопределены.
@BeforeAll Выполняется один раз перед всеми методами @Test, @RepeatedTest, @ParameterizedTest или @TestFactory. Этот метод аналогичен методу @BeforeClass в JUnit 4. Методы @BeforeAll могут быть унаследованными (за исключением скрытых или перекрытых методов), и должны быть статическими (кроме случаев использования «per-class» жизненного цикла тестового экземпляра).
@AfterAll Выполняется один раз после всех методов @Test, @RepeatedTest, @ParameterizedTest или @TestFactory. Этот метод аналогичен методу @AfterClass в JUnit 4. Методы @AfterAll могут быть унаследованными (за исключением скрытых или перекрытых методов), и должны быть статическими (кроме случаев использования «per-class» жизненного цикла тестового экземпляра).
@Nested Указывает на то, что класс с аннотацией является вложенным нестатическим тестовым классом. Методы @BeforeAll и @AfterAll нельзя использовать непосредственно в классах @Nested (кроме случаев использования «per-class» жизненного цикла тестового экземпляра). Такие аннотации нельзя унаследовать.
@Tag Используется для присвоения тегов классам или методам для фильтрации тестов. Эта аннотация похожа на группы тестов в TestNG или категории в JUnit 4. Эту аннотацию можно использовать только на уровне класса, но не на уровне метода.
@Disabled Отключает тестовые классы или методы. Эта аннотация аналогична @Ignore в JUnit 4. Такие аннотации нельзя унаследовать.
@ExtendWith Регистрирует пользовательские расширения. Такие аннотации нельзя унаследовать.

Методы, аннотированные @Test, @TestTemplate, @RepeatedTest, @BeforeAll, @AfterAll, @BeforeEach или @AfterEach, не должны иметь возвращаемого значения и должны быть методами public void XXX(args...){...}. Перевод текста на русский язык:

3.4.1. Сторонние библиотеки утверждений

Хотя инструментов утверждения, предоставляемых JUnit Jupiter, достаточно для многих сценариев тестирования, иногда требуются дополнительные функции, такие как matchers. В таких случаях команда JUnit рекомендует использовать сторонние библиотеки утверждений, такие как AssertJ, Hamcrest и Truth. Разработчики могут свободно выбирать библиотеку утверждений по своему усмотрению.

Например, можно использовать matchers и комбинацию fluent API, чтобы сделать утверждения более описательными и читаемыми. Однако класс утверждений org.junit.jupiter.Assertions в JUnit Jupiter не предоставляет метод assertThat(), который принимает Hamcrest Matcher, как это делает org.junit.Assert в JUnit 4. Вместо этого JUnit Jupiter поощряет разработчиков использовать встроенную поддержку matchers, предоставляемую сторонними библиотеками утверждений.

Следующий пример показывает, как использовать поддержку Hamcrest в тестах JUnit Jupiter. Достаточно добавить библиотеку Hamcrest в путь к классам, чтобы статически импортировать методы, такие как assertThat(), is() и equalTo(), а затем выполнить тестирование в методе assertWithHamcrestMatcher(), как показано ниже.

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.Test;

class HamcrestAssertionDemo {

    @Test
    void assertWithHamcrestMatcher() {
        assertThat(2 + 1, is(equalTo(3)));
    }

}

Конечно, вы можете продолжать использовать программирование на основе JUnit 4, используя org.junit.Assert#assertThat.

3.5. Предположения (Assumptions)

JUnit Jupiter включает подмножество методов предположений (assumptions), предоставляемых JUnit 4, и добавляет некоторые методы, которые хорошо используют лямбда-выражения Java 8. Все предположения JUnit Jupiter являются статическими методами класса org.junit.jupiter.Assumptions.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;

import org.junit.jupiter.api.Test;

class AssumptionsDemo
``` ```
    @Test
    void testOnlyOnCiServer() {
        assumeTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }

    @Test
    void testOnlyOnDeveloperWorkstation() {
        assumeTrue("DEV".equals(System.getenv("ENV")),
            () -> "Aborting test: not on developer workstation");
        // remainder of test
    }

    @Test
    void testInAllEnvironments() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                // perform these assertions only on the CI server
                assertEquals(2, 2);
            });

        // perform these assertions in all environments
        assertEquals("a string", "a string");
    }

3.6. Отключённые тесты (Disabled)

Это пример теста класса отключённых тестов.

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
class DisabledClassDemo {
    @Test
    void testWillBeSkipped() {
    }
}

Это пример метода отключённого теста.

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class DisabledTestsDemo {

    @Disabled
    @Test
    void testWillBeSkipped() {
    }

    @Test
    void testWillBeExecuted() {
    }
}

3.7. Метки и фильтрация (Tagging and Filtering)

Можно пометить тестовые классы и методы. Эти метки могут быть использованы позже для фильтрации test discovery and execution (обнаружение и выполнение тестов).

3.7.1. Синтаксические правила для меток (Syntax Rules for Tags)

  • Метки не должны быть null или blank.
  • Усечённая метка (trimmed tag) не должна содержать пробелов.
  • Усечённая метка не должна содержать ISO управляющие символы.
  • Усечённая метка не должна содержать следующие зарезервированные символы:     , , ( , ) , & , | , !
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("fast")
@Tag("model")
class TaggingDemo {

    @Test
    @Tag("taxes")
    void testingTaxCalculation() {
    }

}

3.8. Жизненный цикл тестовых экземпляров (Test Instance Lifecycle)

Для изолированного выполнения отдельных тестовых методов и избежания неожиданных побочных эффектов из-за изменяемого состояния тестового экземпляра, JUnit создаёт новый экземпляр тестового класса перед выполнением каждого тестового метода. Этот «per-method» жизненный цикл тестового экземпляра является поведением по умолчанию в JUnit Jupiter, аналогично всем предыдущим версиям JUnit.

Если вы хотите, чтобы JUnit Jupiter выполнял все тестовые методы на одном и том же тестовом экземпляре, просто аннотируйте свой тестовый класс с помощью @Testinstance(Lifecycle.PER_CLASS). Когда используется этот режим, каждый тестовый класс создаст новый тестовый экземпляр. Поэтому, если ваши тестовые методы зависят от состояния, хранящегося в переменных экземпляра, вам может потребоваться сбросить это состояние в методах @Beforeeach или @Aftereach.

Режим «per-class» имеет несколько дополнительных преимуществ по сравнению с режимом по умолчанию «per-method». В частности, в режиме «per-class», можно объявлять методы @Beforeall и @Afterall на нестатических методах и интерфейсных методах по умолчанию. Таким образом, режим «per-class» также позволяет использовать методы @Beforeall и @Afterall в тестовых классах @Nested.

Если вы используете язык программирования Kotlin для написания тестов, вы также можете обнаружить, что переключение на режим жизненного цикла «per-class» облегчает реализацию методов @Beforeall и @Afterall.

В жизненном цикле тестовых экземпляров, тестовые методы — это методы, аннотированные с помощью @Test, @RepeatedTest, @ParameterizedTest, @TestFactory или @TestTemplate.

3.8.1. Изменение жизненного цикла тестовых экземпляров по умолчанию (Changing the Default Test Instance Lifecycle)

Если тестовый класс или тестовый интерфейс не аннотированы с помощью @TestInstance, JUnit Jupiter будет использовать жизненный цикл по умолчанию. Стандартным значением по умолчанию является PER_METHOD; однако, можно изменить значение по умолчанию для всего тестового плана. Чтобы изменить жизненный цикл тестовых экземпляров по умолчанию, просто установите параметр конфигурации junit.jupiter.testinstance.lifecycle.default в значение перечисления TestInstance.Lifecycle, где имя перечисления игнорирует регистр. Это можно сделать как системное свойство JVM при запуске, как параметр конфигурации в LauncherDiscoveryRequest или через файл конфигурации платформы JUnit (см. раздел «Конфигурационные параметры» (Configuration Parameters)).

Например, чтобы установить жизненный цикл тестовых экземпляров по умолчанию равным Lifecycle.PER_CLASS, можно использовать следующее системное свойство при запуске JVM: -Djunit.jupiter.testinstance.lifecycle.default=per_class

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

Чтобы установить жизненный цикл тестовых экземпляров по умолчанию равным Lifecycle.PER_CLASS с использованием файла конфигурации платформы JUnit, создайте файл с именем junit-platform.properties в корне пути к классам (например, src/test/resources), содержащий следующее содержимое: junit.jupiter.testinstance.lifecycle.default = per_class

Изменение жизненного цикла тестовых экземпляров по умолчанию может привести к непредсказуемым результатам и хрупким сборкам, если они не согласованы. Например, если сборка настроена на использование «per-class» семантики по умолчанию, но IDE выполняет тесты с «per-method» семантикой, это затруднит отладку ошибок на сервере сборки. Рекомендуется изменять значение по умолчанию через файл конфигурации JUnit Platform, а не через системные свойства JVM. ``` import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test;

@DisplayName("A stack") class TestingAStackDemo {

Stack<Object> stack;

@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
    new Stack<>();
}

@Nested
@DisplayName("when new")
class WhenNew {

    @BeforeEach
    void createNewStack() {
        stack = new Stack<>();
    }

    @Test
    @DisplayName("is empty")
    void isEmpty() {
        assertTrue(stack.isEmpty());
    }

    @Test
    @DisplayName("throws EmptyStackException when popped")
    void throwsExceptionWhenPopped() {
        assertThrows(EmptyStackException.class, () -> stack.pop());
    }

    @Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        assertThrows(EmptyStackException.class, () -> stack.peek());
    }

    @Nested
    @DisplayName("after pushing an element")
    class AfterPushing {

        String anElement = "an element";

        @BeforeEach
        void pushAnElement() {
            stack.push(anElement);
        }

        @Test
        @DisplayName("it is no longer empty")
        void isNotEmpty() {
            assertFalse(stack.isEmpty());
        }

        @Test
        @DisplayName("returns the element when popped and is empty")
        void returnElementWhenPopped() {
            assertEquals(anElement, stack.pop());
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("returns the element when peeked but remains not empty")
        void returnElementWhenPeeked() {
            assertEquals(anElement, stack.peek());
            assertFalse(stack.isEmpty());
        }
    }
}

}


**Примечание**: в тексте запроса присутствуют фрагменты кода на языке Java, которые были переведены на русский язык. ```
testInfo) {
    assertEquals("TEST 1", testInfo.getDisplayName());
    assertTrue(testInfo.getTags().contains("my-tag"));
}

@Test
void test2() {
}

RepetitionInfoParameterResolver (повторение информации, параметр, анализатор):

Если метод имеет параметр типа @RepeatedTest, @BeforeEach или @AfterEach, то RepetitionInfoParameterResolver предоставит экземпляр RepetitionInfo. Затем можно использовать информацию о повторении для получения информации о текущем повторении и соответствующем количестве повторений @RepeatedTest. Однако следует отметить, что RepetitionInfoParameterResolver не может быть зарегистрирован вне контекста @RepeatedTest. Смотрите примеры повторяющихся тестов (Repeated Test Examples).

TestReporterParameterResolver (анализатор параметров тестового репортёра):

Если тип параметра метода — TestReporter, TestReporterParameterResolver предоставит экземпляр. Можно использовать TestReporter для публикации дополнительных данных о текущей тестовой сессии. Данные могут быть использованы TestExecutionListener.reportingEntryPublished(), поэтому их можно просматривать в IDE или включать в отчёты.

В JUnit Jupiter вы должны использовать TestReporter. В JUnit 4 вы можете печатать информацию на stdout или stderr. Используйте @RunWith(JUnitPlatform.class), чтобы вывести все зарегистрированные записи на stdout.

import java.util.HashMap;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestReporter;

class TestReporterDemo {

    @Test
    void reportSingleValue(TestReporter testReporter) {
        testReporter.publishEntry("a key", "a value");
    }

    @Test
    void reportSeveralValues(TestReporter testReporter) {
        HashMap<String, String> values = new HashMap<>();
        values.put("user name", "dk38");
        values.put("award year", "1974");

        testReporter.publishEntry(values);
    }
}

Другие анализаторы параметров должны быть явно включены через @ExtendWith для соответствующего расширения (extensions).

Далее приведён пример пользовательского анализатора параметров (ParameterResolver), который проверяет MockitoExtension (https://github.com/junit-team/junit5-samples/tree/r5.0.2/junit5-mockito-extension/src/main/java/com/example/mockito/MockitoExtension.java). Хотя он не предназначен для коммерческого использования, он демонстрирует простоту и эффективность модели расширения и процесса анализа параметров. MyMockitoTest показывает, как модель Mockito внедряется в методы @BeforeEach и @Test.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import com.example.Person;
import com.example.mockito.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class MyMockitoTest {

    @BeforeEach
    void init(@Mock Person person) {
        when(person.getName()).thenReturn("Dilbert");
    }

    @Test
    void simpleTestWithInjectedMock(@Mock Person person) {
        assertEquals("Dilbert", person.getName());
    }
}

3.11. Тестирование интерфейсов и методов по умолчанию (Test Interfaces and Default Methods)

JUnit Jupiter позволяет объявлять @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, @TestTemplate, @BeforeEach, @AfterEach в интерфейсных методах default. Если тестируемый интерфейс или класс помечен @TestInstance(Lifecycle.PER_CLASS), то @BeforeAll и @AfterAll могут быть объявлены в статических методах интерфейса или методах default. Вот несколько примеров.

@TestInstance(Lifecycle.PER_CLASS)
interface TestLifecycleLogger {

    static final Logger LOG = Logger.getLogger(TestLifecycleLogger.class.getName());

    @BeforeAll
    default void beforeAllTests() {
        LOG.info("Before all tests");
    }

    @AfterAll
    default void afterAllTests() {
        LOG.info("After all tests");
    }

    @BeforeEach
    default void beforeEachTest(TestInfo testInfo) {
        LOG.info(() -> String.format("About to execute [%s]",
            testInfo.getDisplayName()));
    }

    @AfterEach
    default void afterEachTest(TestInfo testInfo) {
        LOG.info(() -> String.format("Finished executing [%s]",
            testInfo.getDisplayName()));
    }
}
interface TestInterfaceDynamicTestsDemo {

    @TestFactory
    default Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
            dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
            dynamicTest("2nd dynamic test in test interface",
``` ```
() -> assertEquals(4, 2 * 2))
        );
    }

}

@ExtendWith 和 @Tag можно использовать для объявления в интерфейсе тестирования, чтобы классы, реализующие интерфейс, автоматически наследовали его метки и расширения. Перед выполнением и после выполнения обратного вызова TimingExtension просмотрите исходный код TimingExtension.

@Tag("timed")
@ExtendWith(TimingExtension.class)
interface TimeExecutionLogger {
}

В вашем тестовом классе вы можете реализовать эти тестовые интерфейсы, чтобы они применялись.

class TestInterfaceDemo implements TestLifecycleLogger,
        TimeExecutionLogger, TestInterfaceDynamicTestsDemo {

    @Test
    void isEqualValue() {
        assertEquals(1, 1, "is always equal");
    }

}

Результат выполнения TestInterfaceDemo выглядит следующим образом:

:junitPlatformTest
INFO  example.TestLifecycleLogger - Before all tests
INFO  example.TestLifecycleLogger - About to execute [dynamicTestsFromCollection()]
INFO  example.TimingExtension - Method [dynamicTestsFromCollection] took 13 ms.
INFO  example.TestLifecycleLogger - Finished executing [dynamicTestsFromCollection()]
INFO  example.TestLifecycleLogger - About to execute [isEqualValue()]
INFO  example.TimingExtension - Method [isEqualValue] took 1 ms.
INFO  example.TestLifecycleLogger - Finished executing [isEqualValue()]
INFO  example.TestLifecycleLogger - After all tests

Test run finished after 190 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         3 tests found           ]
[         0 tests skipped         ]
[         3 tests started         ]
[         0 tests aborted         ]
[         3 tests successful      ]
[         0 tests failed          ]

BUILD SUCCESSFUL

Ещё одно возможное применение этой функции — написание тестов для контрактов интерфейса. Например, вы можете написать тесты для реализации Object.equals или Comparable.compareTo.

public interface Testable<T> {

    T createValue();

}
public interface EqualsContract<T> extends Testable<T> {

    T createNotEqualValue();

    @Test
    default void valueEqualsItself() {
        T value = createValue();
        assertEquals(value, value);
    }

    @Test
    default void valueDoesNotEqualNull() {
        T value = createValue();
        assertFalse(value.equals(null));
    }

    @Test
    default void valueDoesNotEqualDifferentValue() {
        T value = createValue();
        T differentValue = createNotEqualValue();
        assertNotEquals(value, differentValue);
        assertNotEquals(differentValue, value);
    }

}
public interface ComparableContract<T extends Comparable<T>> extends Testable<T> {

    T createSmallerValue();

    @Test
    default void returnsZeroWhenComparedToItself() {
        T value = createValue();
        assertEquals(0, value.compareTo(value));
    }

    @Test
    default void returnsPositiveNumberComparedToSmallerValue() {
        T value = createValue();
        T smallerValue = createSmallerValue();
        assertTrue(value.compareTo(smallerValue) > 0);
    }

    @Test
    default void returnsNegativeNumberComparedToSmallerValue() {
        T value = createValue();
        T smallerValue = createSmallerValue();
        assertTrue(smallerValue.compareTo(value) < 0);
    }

}

В своём тестовом классе вы можете реализовать два контракта, тем самым наследуя соответствующие тесты. Конечно, вы должны реализовать абстрактные методы.

class StringTests implements ComparableContract<String>, EqualsContract<String> {

    @Override
    public String createValue() {
        return "foo";
    }

    @Override
    public String createSmallerValue() {
        return "bar"; // 'b' < 'f' in "foo"
    }

    @Override
    public String createNotEqualValue() {
        return "baz";
    }

}

Обратите внимание, что приведённый выше тест является лишь примером и поэтому не является полным.

3.12. Повторяющиеся тесты (Repeated Tests)

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

Следующий пример показывает, как объявить метод с именем repeatedTest(), который будет автоматически повторяться 10 раз.

@RepeatedTest(10)
void repeatedTest() {
    // ...
}

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

  • {displayName}: отображает имя метода @RepeatedTest
  • {currentRepetition}: текущий счётчик повторений
  • {totalRepetitions}: общее количество повторений {totalRepetitions}"。Таким образом, для ранее использованного отображаемого имени repeatedTest()` примеры будут такими: повторение 1 из 10, повторение 2 из 10 и так далее.

Если вы хотите, чтобы отображаемое имя метода @RepeatedTest содержало повторяющееся имя, вы можете определить свой собственный шаблон или использовать предопределённый шаблон RepeatedTest.LONG_DISPLAY_NAME. Последний равен "{displayName} :: repetition {currentRepetition} of {totalRepetitions}". Это позволит отображать отдельные повторяющиеся имена, такие как repeatedTest() :: повторение 1 из 10, repeatedTest() :: повторение 2 из 10 и так далее.

3.12.1. Примеры повторяющихся тестов (Repeated Test Examples)

В конце этого раздела класс RepeatedTestsDemo демонстрирует несколько примеров повторяющихся тестов.

Метод repeatedTest() аналогичен примерам из предыдущего раздела; однако repeatedTestWithRepetitionInfo() показывает, как можно получить доступ к общему количеству повторений текущего повторяющегося теста с помощью экземпляра, внедрённого в @RepeatedTest, @BeforeEach или @AfterEach.

Следующие два метода показывают, как включить пользовательское отображаемое имя @DisplayName в каждое повторяющееся отображаемое имя для метода @RepeatedTest. Метод customDisplayName() объединяет пользовательское отображаемое имя с пользовательским шаблоном, а затем использует TestInfo для проверки формата сгенерированного отображаемого имени. Repeat! (повторить!) происходит от объявления @DisplayName {displayName}, а 1 / 1 происходит от {currentRepetition}/{totalRepetitions}. В отличие от этого, метод customDisplayNameWithLongPattern() использует ранее упомянутый предопределенный шаблон RepeatedTest.LONG_DISPLAY_NAME.

repeatedTestInGerman() демонстрирует возможность преобразования отображаемых имён повторяющихся тестов на иностранные языки — в этом примере на немецкий язык — для отдельных повторяющихся имён, таких как Wiederholung 1 von 5, Wiederholung 2 von 5 и т. д.

Поскольку метод beforeEach() аннотирован @BeforeEach, он будет выполняться перед каждым повторением каждого повторяющегося теста. Внедряя TestInfo и RepetitionInfo в этот метод, мы можем получить информацию о текущем выполнении повторяющегося теста. При выполнении повторяющихся тестов на уровне информации журнала вывод будет следующим:

INFO: About to execute repetition 1 of 10 for repeatedTest
INFO: About to execute repetition 2 of 10 for repeatedTest
...
INFO: About to execute repetition 9 of 10 for repeatedTest
INFO: About to execute repetition 10 of 10 for repeatedTest

INFO: About to execute repetition 1 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo
...
INFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo

INFO: About to execute repetition 1 of 1 for customDisplayName
INFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern

INFO: About to execute repetition 1 of 5 for repeatedTestInGerman
INFO: About to execute repetition 2 of 5 for repeatedTestInGerman
...
INFO: About to execute repetition 4 of 5 for repeatedTestInGerman
INFO: About to execute repetition 5 of 5 for repeatedTestInGerman
``` Вот перевод текста на русский язык:

@DisplayName("Details...") void customDisplayNameWithLongPattern(TestInfo testInfo) { assertEquals(testInfo.getDisplayName(), "Details... :: repetition 1 of 1"); }

@RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}") void repeatedTestInGerman() { // ... } }


При использовании плагинов `ConsoleLauncher` или `junitPlatformTest` была включена тема unicode, и при последующем выводе были выполнены результаты `RepeatedTestsDemo`.

```java
├─ RepeatedTestsDemo ✔
│  ├─ repeatedTest() ✔
│  │  ├─ repetition 1 of 10 ✔
│  │  ├─ repetition 2 of 10 ✔
│  │  ├─ repetition 3 of 10 ✔
│  │  ├─ repetition 4 of 10 ✔
│  │  ├─ repetition 5 of 10 ✔
│  │  ├─ repetition 6 of 10 ✔
│  │  ├─ repetition 7 of 10 ✔
│  │  ├─ repetition 8 of 10 ✔
│  │  ├─ repetition 9 of 10 ✔
│  │  └─ repetition 10 of 10 ✔
│  ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ✔
│  │  ├─ repetition 1 of 5 ✔
│  │  ├─ repetition 2 of 5 ✔
│  │  ├─ repetition 3 of 5 ✔
│  │  ├─ repetition 4 of 5 ✔
│  │  └─ repetition 5 of 5 ✔
│  ├─ Repeat! ✔
│  │  └─ Repeat! 1/1 ✔
│  ├─ Details... ✔
│  │  └─ Details... :: repetition 1 of 1 ✔
│  └─ repeatedTestInGerman() ✔
│     ├─ Wiederholung 1 von 5 ✔
│     ├─ Wiederholung 2 von 5 ✔
│     ├─ Wiederholung 3 von 5 ✔
│     ├─ Wiederholung 4 von 5 ✔
│     └─ Wiederholung 5 von 5 ✔

3.13. Параметризованные тесты (Parameterized Tests)

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

Внимание: параметризованные тесты в настоящее время являются экспериментальной функцией. Для получения дополнительной информации см. таблицу в разделе «Экспериментальные API» (Experimental APIs).

@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
    assertTrue(isPalindrome(candidate));
}

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

palindromes(String) 
├─ [1] racecar 
├─ [2] radar 
└─ [3] able was I ere I saw elba 

3.13.1. Необходимые настройки (Required Setup)

Чтобы использовать параметризованные тесты, вам необходимо добавить зависимость от компонента junit-jupiter-params. Подробнее см. метаданные зависимостей (Dependency Metadata).

3.13.2. Источники параметров (Sources of Arguments)

JUnit Jupiter предоставляет множество источников аннотаций прямо из коробки. В каждом следующем разделе даётся краткое описание и пример для каждого раздела. См. JavaDoc пакета org.junit.jupiter.params.provider. Этот пакет предоставляет дополнительную информацию о поставщиках.

@ValueSource

Возможно, @ValueSource — самый простой из доступных источников. Он позволяет указать набор литералов примитивных типов (String, int, long или double) и может предоставлять только один параметр за раз.

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertNotNull(argument);
}

@EnumSource

Аннотация @EnumSource предоставляет удобный способ использования констант Enum. Эта аннотация имеет необязательный параметр names, который позволяет вам указать, какие константы использовать. Если он опущен, все константы будут использоваться в следующем примере.

@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnumSource(TimeUnit timeUnit) {
    assertNotNull(timeUnit);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = { "DAYS", "HOURS" })
void testWithEnumSourceInclude(TimeUnit timeUnit) {
    assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}

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

@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = EXCLUDE, names = { "DAYS", "HOURS" })
void testWithEnumSourceExclude(TimeUnit timeUnit) {
    assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
    assertTrue(timeUnit.name().length() > 5);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = MATCH_ALL, names = "^(M|N).+SECONDS$")
void testWithEnumSourceRegex(TimeUnit timeUnit) {
    String name = timeUnit.name();
    assertTrue(name.startsWith("M") || name.startsWith("N"));
    assertTrue(name.endsWith("SECONDS"));
}

@MethodSource

Аннотация @MethodSource позволяет ссылаться на один или несколько фабричных методов в классе теста. Эти методы должны возвращать поток (Stream), итератор (Iterator) или массив параметров. Кроме того, эти методы не могут принимать никаких параметров. По умолчанию эти методы должны быть статическими (static), если только класс теста не аннотирован @TestInstance(Lifecycle.PER_CLASS).

Если требуется только один параметр, можно вернуть поток типа параметра, как показано в следующем примере. ТаймЮнит.СЕКУНДЫ | java.time.Instant | "1970-01-01T00:00:00Z" → Instant.ofEpochMilli(0)

TimeUnit.SECONDS — это класс в Java, который представляет собой единицу времени.

java.time.Instant — класс для представления момента на временной шкале.

"1970-01-01T00:00:00Z" — дата и время в формате ISO 8601.

→ Instant.ofEpochMilli(0) — метод, который создаёт экземпляр класса Instant из количества миллисекунд с начала эпохи Unix (1 января 1970 года).

java.time.LocalDate | "2017-03-14" → LocalDate.of(2017, 3, 14)

LocalDate — класс для работы с датой без учёта времени суток.

"2017-03-14" — дата в формате год-месяц-день.

→ LocalDate.of(2017, 3, 14) — статический фабричный метод, создающий экземпляр LocalDate из года, месяца и дня.

java.time.LocalDateTime | "2017-03-14T12:34:56.789" → LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)

LocalDateTime — класс для хранения даты и времени без часового пояса.

"2017-03-14T12:34:56.789" — дата и время в формате год-месяц-деньTчас:минута:секунда.dotmillis.

→ LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000) — статический метод, создающий объект LocalDateTime из года, месяца, дня, часа, минуты, секунды и наносекунд.

java.time.LocalTime | "12:34:56.789" → LocalTime.of(12, 34, 56, 789_000_000)

LocalTime — класс, представляющий время суток без учёта даты.

"12:34:56.789" — время в формате час:минута:секунда.dotmillis.

→ LocalTime.of(12, 34, 56, 789_000_000) — статический метод создания объекта LocalTime из часа, минуты, секунды и наносекунд.

java.time.OffsetDateTime | "2017-03-14T12:34:56.789Z" → OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

OffsetDateTime — класс, объединяющий дату, время и смещение часового пояса.

"2017-03-14T12:34:56.789Z" — дата, время и часовой пояс в формате год-месяц-деньTчас:минута:секунда.dotmillisZ.

→ OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC) — статический метод создания экземпляра OffsetDateTime из года, месяца, дня, часа, минуты, секунды, наносекунд и смещения часового пояса UTC.

java.time.OffsetTime | "12:34:56.789Z" → OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)

OffsetTime — класс для представления времени суток с учётом смещения часового пояса.

"12:34:56.789Z" — время и часовой пояс в формате час:минута:секунда.dotmillisZ.

→ OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC) — статический метод создания OffsetTime из часа, минуты, секунды, наносекунд и часового пояса UTC.

java.time.Year | "2017" → Year.of(2017)

Year — класс для представления года.

"2017" — год.

→ Year.of(2017) — статический метод получения объекта Year из года.

java.time.YearMonth | "2017-03" → YearMonth.of(2017, 3)

YearMonth — класс для объединения года и месяца.

"2017-03" — год и месяц в формате год-месяц.

→ YearMonth.of(2017, 3) — статический метод создания YearMonth из года и месяца.

java.time.ZonedDateTime | "2017-03-14T12:34:56.789Z" → ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneId.of("UTC"))

ZonedDateTime — класс для представления даты, времени и часового пояса.

"2017-03-14T12:34:56.789Z" — дата, время и часовой пояс в формате год-месяц-деньTчас:минута:секунда.dotmillisZ.

→ ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneId.of("UTC")) — статический метод создания ZonedDateTime из года, месяца, дня, часа, минуты, секунды, наносекунд, часового пояса с идентификатором «UTC». Динамические тесты (Dynamic Tests)

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

Помимо этих стандартных тестов, JUnit Jupiter также вводит совершенно новую модель программирования для тестирования. Этот новый тест является динамическим тестом, который генерируется во время выполнения с помощью фабричного метода, аннотированного @TestFactory.

По сравнению с методом @Test, метод @TestFactory сам по себе не является тестовым примером, а является фабрикой для тестовых примеров. Таким образом, динамический тест — это продукт фабрики. Технически, метод @TestFactory должен возвращать экземпляр DynamicNode типа Stream, Collection или Iterable. Экземпляры DynamicNode включают подклассы DynamicContainer и DynamicTest. Экземпляр DynamicContainer состоит из отображаемого имени и списка динамических дочерних узлов и может создавать произвольно вложенные структуры динамических узлов. Затем экземпляр DynamicTest будет отложен для выполнения, поддерживая динамическое и даже неопределённое создание тестовых случаев.

Любой поток (Stream), возвращаемый @TestFactory, будет правильно закрыт вызовом stream.close(), что позволяет безопасно использовать ресурсы, такие как Files.lines().

Как и метод @Test, метод @TestFactory не может быть закрытым (private) или статическим (static), и параметры могут быть выборочно объявлены для анализа с помощью параметрических анализаторов (ParameterResolvers).

DynamicTest — это тестовый пример, созданный во время выполнения, состоящий из одного отображаемого имени. Executable. Executable — это функциональный интерфейс (@FunctionalInterface), что означает, что реализация динамического теста может использоваться как лямбда-выражение или ссылка на метод.

Примечание: жизненный цикл динамического тестирования

Жизненный цикл выполнения динамического теста полностью отличается от жизненного цикла выполнения стандартного тестового примера @Test. В частности, для отдельного динамического теста нет обратных вызовов жизненного цикла. Это означает, что методы @BeforeEach и @AfterEach и соответствующие расширения обратного вызова выполняются для метода @TestFactory, а не для каждого выполняемого динамического теста. Другими словами, если вы обращаетесь к полю из лямбда-выражения в тестовом экземпляре из тестового экземпляра, эти поля не будут сброшены обратными вызовами методов или расширений, выполняемых одним и тем же методом @TestFactory, создающим отдельный динамический тест.

В JUnit Jupiter 5.0.2 динамические тесты всегда должны создаваться фабричным методом; однако это может быть дополнено инструментами регистрации в более поздних версиях.

Обратите внимание, что динамическое тестирование в настоящее время является экспериментальной функцией. Для получения дополнительной информации см. таблицу экспериментальных API.

Пример динамического тестирования (Dynamic Test Examples)

Класс DynamicTestsDemo демонстрирует несколько примеров фабрик тестов и динамических тестов.

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

Следующие пять методов представляют собой простые примеры создания Collection, Iterable, Iterator или экземпляров Stream DynamicTest. Большинство этих примеров не демонстрируют реального динамического поведения, а просто показывают поддерживаемые типы возврата в принципе. Однако dynamicTestsFromStream() и dynamicTestsFromIntStream() показывают, насколько легко генерировать динамические тесты для данной строки или серии входных чисел.

Следующий метод представляет собой настоящий динамический тест. Метод generateRandomNumberOfTests() реализует Iterator, который генерирует случайные числа, генератор отображаемых имён и исполнитель тестов, а затем предоставляет все три DynamicTest.stream(). Хотя недетерминированное поведение generateRandomNumberOfTests() конфликтует с тестированием повторяемости, его следует использовать с осторожностью, но оно демонстрирует силу динамического тестирования.

Последний метод использует DynamicContainer для создания вложенной структуры динамических тестовых уровней. { /* ... */ })); }

@TestFactory
Stream<DynamicTest> dynamicTestsFromIntStream() {
    // Генерирует тесты для первых 10 чётных целых чисел.
    return IntStream.iterate(0, n -> n + 2).limit(10)
        .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0)));
}

@TestFactory
Stream<DynamicTest> generateRandomNumberOfTests() {

    // Генерирует случайные положительные целые числа от 0 до 100 до тех пор,
    // пока не встретится число, которое делится на 7 без остатка.
    Iterator<Integer> inputGenerator = new Iterator<Integer>() {

        Random random = new Random();
        int current;

        @Override
        public boolean hasNext() {
            current = random.nextInt(100);
            return current % 7 != 0;
        }

        @Override
        public Integer next() {
            return current;
        }
    };

    // Создаёт имена отображения вида: input:5, input:37, input:85 и т. д.
    Function<Integer, String> displayNameGenerator = (input) -> "input:" + input;

    // Выполняет тесты на основе текущего входного значения.
    ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0);

    // Возвращает поток динамических тестов.
    return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);
}

@TestFactory
Stream<DynamicNode> dynamicTestsWithContainers() {
    return Stream.of("A", "B", "C")
        .map(input -> dynamicContainer("Container " + input, Stream.of(
            dynamicTest("not null", () -> assertNotNull(input)),
            dynamicContainer("properties", Stream.of(
                dynamicTest("length > 0", () -> assertTrue(input.length() > 0)),
                dynamicTest("not empty", () -> assertFalse(input.isEmpty()))
            ))
        )));
}

4. Запуск тестов  

4.1. Поддержка IDE

4.1.1. IntelliJ IDEA

IntelliJ IDEA поддерживает запуск тестов на JUnit Platform начиная с версии 2016.2 и выше. Более подробную информацию можно найти в блоге IntelliJ IDEA.

Таблица 1. Версии JUnit 5 и их совместимость с IntelliJ IDEA

Версия IntelliJ IDEA Связанная версия JUnit 5
2016.2 M2
2016.3.1 M3
2017.1.2 M4
2017.2.1 M5
2017.2.3 RC2

Предупреждение | IntelliJ IDEA поставляется с определённой версией JUnit 5. Это означает, что использование более новой версии Jupiter API может привести к неудачному выполнению этих тестов. После выпуска первой GA-версии JUnit 5 эта проблема будет решена. Тем временем рекомендуется использовать новую версию JUnit 5 вместо той, которая поставляется с IntelliJ IDEA.

Чтобы использовать другую версию JUnit 5, необходимо вручную добавить jar-файлы junit-platform-launcher, junit-jupiter-engine и junit-vintage-engine в путь к классам.

Добавление дополнительных зависимостей Gradle

// Только для использования в IntelliJ IDEA, который поставляется со старой версией
testRuntime("org.junit.platform:junit-platform-launcher:1.0.2")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.2")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.2")

Добавление дополнительных зависимостей Maven

<!-- Только для использования в IntelliJ IDEA, который поставляется со старой версией -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>4.12.2</version>
    <scope>test</scope>
</dependency>

4.1.2. Eclipse Beta Support

Eclipse 4.7 (Oxygen) beta и более поздние версии поддерживают JUnit Platform и JUnit Jupiter. Подробные инструкции по настройке можно найти на странице Eclipse JDT UI/JUnit 5 wiki.

4.1.3. Другие IDEs

На момент написания этой статьи, помимо использования IntelliJ IDEA или Eclipse, прямой поддержки запуска тестов на платформе JUnit в IDE не было. Однако команда JUnit предоставила два промежуточных решения, которые позволяют вам попробовать JUnit 5 в современных IDE. Вы можете вручную запустить тесты с помощью консольного запуска или использовать Runner на базе JUnit 4 для выполнения тестов.

4.2. Поддержка сборки (Build Support)

Gradle

Команда JUnit разработала базовый плагин Gradle, который позволяет запускать тесты любого типа, поддерживаемые тестовым движком (например, JUnit 3, JUnit 4, JUnit Jupiter, Specsy и т. д.). Это можно увидеть в проекте junit5-gradle-consumer в качестве примера плагина.

Использование плагина JUnit Gradle

Чтобы использовать плагин JUnit Gradle, сначала убедитесь, что вы используете Gradle версии 2.5 или выше. После этого можно настроить build.gradle следующим образом:

buildscript {
    repositories {
        mavenCentral()
        // The following is only necessary if you want to use SNAPSHOT releases.
        // maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    }
    dependencies {
        classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2'
    }
}

apply plugin: 'org.junit.platform.gradle.plugin'

Настройка плагина JUnit Gradle

После применения плагина JUnit Gradle его можно настроить следующим образом:

junitPlatform {
    platformVersion '1.0.2' // optional, defaults to plugin version
    logManager 'org.apache.logging.log4j.jul.LogManager'
    reportsDir file('build/test-results/junit-platform') // this is the default
    // enableStandardTestTask true
    // selectors (optional)
    // filters (optional)
}

Установка logManager указывает полностью определённое имя класса для java.util.logging.manager, предоставляемого JUnit Gradle плагином для использования java.util.logging.LogManager. В приведённом примере показано, как настроить log4j в качестве logManager.

По умолчанию плагин отключает стандартную задачу Gradle для тестирования, но её можно включить с помощью флага enableStandardTestTask.

Конфигурация селекторов

По умолчанию плагин сканирует выходной каталог вашего проекта для выполнения тестов. Однако вы можете указать использование селекторов (selectors) для явного выполнения определённых тестов.

junitPlatform {
    // ...
    selectors {
        uris 'file:///foo.txt', 'http://example.com/'
        uri 'foo:resource'  (1)
        files 'foo.txt', 'bar.csv'
        file 'qux.json'  (2)
        directories 'foo/bar', 'bar/qux'
        directory 'qux/bar'  (3)
        packages 'com.acme.foo', 'com.acme.bar'
        aPackage 'com.example.app'  (4)
        classes 'com.acme.Foo', 'com.acme.Bar'
        aClass 'com.example.app.Application'  (5)
        methods 'com.acme.Foo#a', 'com.acme.Foo#b'
        method 'com.example.app.Application#run(java.lang.String[])'  (6)
        resources '/bar.csv', '/foo/input.json'
        resource '/com/acme/my.properties'  (7)
    }
    // ...
}
1. URIs
2. Локальные файлы
3. Локальные каталоги
4. Пакеты
5. Классы, полностью определённые имена классов
6. Методы, полностью определённые имена методов (см. selectMethod(String) в DiscoverySelectors)
7. Ресурсы из пути к классам

Конфигурация фильтров

Вы можете использовать фильтры (filters) для настройки фильтров для тестовых планов. По умолчанию все движки и метки включены в тестовый план, за исключением default includeClassNamePattern (^.*Tests?). Вы можете переопределить шаблон по умолчанию в следующем примере. Когда вы указываете несколько шаблонов, они объединяются семантически.

junitPlatform {
    // ...
    filters {
        engines {
            include 'junit-jupiter'
            // exclude 'junit-vintage'
        }
        tags {
            include 'fast', 'smoke'
            // exclude 'slow', 'ci'
        }
        packages {
            include 'com.sample.included1', 'com.sample.included2'
            // exclude 'com.sample.excluded1', 'com.sample.excluded2'
        }
        includeClassNamePattern '.*Spec'
        includeClassNamePatterns '.*Test', '.*Tests'
    }
    // ...
}

Если вы предоставляете идентификатор тестового движка через engines {include …} или engines {exclude …}, JUnit Gradle плагин будет выполнять только тесты, необходимые для этого тестового движка. Аналогично, если вы предоставляете метку через tags {include …} или tags {exclude …}, JUnit Gradle плагин выполнит только соответствующие тесты (например, на основе @Tag аннотации JUnit Jupiter). То же самое относится к именам пакетов, которые могут быть включены или исключены с использованием packages {include …} или packages {exclude …}.

Конфигурация параметров

Вы можете установить параметры конфигурации, чтобы влиять на обнаружение и выполнение тестов, используя configurationParameter или configurationParameters DSL. Первый используется для установки одного параметра конфигурации, а второй — для установки нескольких пар ключ-значение одновременно. Все ключи и значения должны быть строками.

junitPlatform {
    // ...
    configurationParameter 'junit.jupiter.conditions.deactivate', '*'
    configurationParameters([
        'junit.jupiter.extensions.autodetection.enabled': 'true',
        'junit.jupiter.testinstance.lifecycle.default': 'per_class'
    ])
    // ...
}

Конфигурирование тестового движка

Для запуска тестов JUnit Gradle плагина необходимо, чтобы TestEngine реализация была доступна в пути к классам.

Чтобы настроить поддержку тестов на основе JUnit Jupiter, настройте зависимость testCompile для JUnit Jupiter API и аналогичную зависимость testRuntime для реализации JUnit Jupiter TestEngine, как показано ниже.

dependencies {
    testCompile("org.junit.jupiter:junit-jupiter-api:5.0.2")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.2")
}

JUnit Gradle плагин может запускать JUnit. 4. Тестирование

Для тестирования необходимо настроить зависимость testCompile от JUnit 4 и зависимость testRuntime от JUnit Vintage TestEngine. Пример:

dependencies {
    testCompile("junit:junit:4.12")
    testRuntime("org.junit.vintage:junit-vintage-engine:4.12.2")
}

Использование JUnit Gradle плагина

После применения и настройки JUnit Gradle плагина появляется новая задача junitPlatformTest.

Вызов gradlew junitPlatformTest из командной строки (или gradlew test) выполняет все тесты проекта, имена классов которых соответствуют регулярному выражению, заданному через includeClassNamePattern (по умолчанию — *^.Tests?$).

Выполнение задачи junitPlatformTest в проекте junit5 -gradle- consumer приводит к следующему результату:

:junitPlatformTest

Test run finished after 93 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         3 tests found           ]
[         1 tests skipped         ]
[         2 tests started         ]
[         0 tests aborted         ]
[         2 tests successful      ]
[         0 tests failed          ]

BUILD SUCCESSFUL

Если тест не пройден, сборка завершается с ошибкой, результат похож на следующий:

:junitPlatformTest

Test failures (1):
  JUnit Jupiter:SecondTest:mySecondTest()
    MethodSource [className = 'com.example.project.SecondTest', methodName = 'mySecondTest', methodParameterTypes = '']
    => Exception: 2 is not equal to 1 ==> expected: <2> but was: <1>

Test run finished after 99 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         3 tests found           ]
[         0 tests skipped         ]
[         3 tests started         ]
[         0 tests aborted         ]
[         2 tests successful      ]
[         1 tests failed          ]

:junitPlatformTest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':junitPlatformTest'.
> Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1

Обратите внимание: если какой-либо контейнер или тест завершается неудачно, значение выхода равно 1, иначе оно равно 0.

Также обратите внимание: результаты любых тестов, запущенных через JUnit Gradle плагин, не включаются в стандартные отчёты о тестах, генерируемые Gradle, но их результаты обычно можно агрегировать на сервере CI. Для этого нужно проверить свойство reportsDir плагина.

4.2.2 Maven

Команда JUnit разработала базовый Maven Surefire, который позволяет запускать тесты JUnit 4 и JUnit Jupiter через mvn test. Проект junit5- maven-consumer показывает, как его использовать, и служит отправной точкой для mavne тестирования.

Обратите внимание: из-за утечки памяти в Surefire 2.20, junit-platform-surefire-provider сейчас использует только Surefire 2.19.1.

Пример файла pom.xml:

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>1.0.2</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...

Конфигурация тестового движка

Чтобы Maven Surefire мог запускать любые тесты, необходимо добавить TestEngine реализацию в путь класса времени выполнения.

Для поддержки основанных на JUnit Jupiter тестов необходимо настроить test зависимость на API JUnit Jupiter и добавить реализацию JUnit Jupiter TestEngine в зависимости maven-surefire-plugin, например:

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>1.0.2</version>
                </dependency>
                <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-engine</artifactId>
                    <version>5.0.2</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
<dependencies> **Консоль запуска**

ConsoleLauncher — это командная строка Java-приложения, которая позволяет запускать JUnit Platform с консоли. Например, она может использоваться для запуска тестов JUnit Vintage и JUnit Jupiter, а результаты выполнения тестов будут выведены на консоль.

Исполняемый файл junit-platform-console-standalone-1.0.2.jar со всеми зависимостями находится в репозитории Maven в каталоге junit-platform-console-standalone. Вы можете запустить отдельный ConsoleLauncher следующим образом:

`java -jar junit-platform-console-standalone-1.0.2.jar` [Options]

Пример вывода:

```java
├─ JUnit Vintage
│  └─ example.JUnit4Tests
│     └─ standardJUnit4Test ✔
└─ JUnit Jupiter
   ├─ StandardTests
   │  ├─ succeedingTest() ✔
   │  └─ skippedTest() ↷ for demonstration purposes
   └─ A special test case
      ├─ Custom test name containing spaces ✔
      ├─ ╯°□°)╯ ✔
      └─ 😱 ✔

Test run finished after 64 ms
[         5 containers found      ]
[         0 containers skipped    ]
[         5 containers started    ]
[         0 containers aborted    ]
[         5 containers successful ]
[         0 containers failed     ]
[         6 tests found           ]
[         1 tests skipped         ]
[         5 tests started         ]
[         0 tests aborted         ] [         5 тестов успешно      ]
[         0 тестов не пройдено     ]

**Обратите внимание:** если любой контейнер или тест завершатся неудачно, `ConsoleLauncher` выйдет с состоянием 1. Иначе состояние выхода будет 0.

#### 4.3.1. Опции
| Опция                                        | Описание                                                   |
| -------------------------------------------|------------------------------------------------------------|
| -h, --help                                    | Отображение справочной информации.                           |
| --disable-ansi-colors                         | Отключение цветов ANSI в выводе (не поддерживается всеми терминалами). |
| --details <[none,flat,tree,verbose]>          | Выбор режима детализации вывода при выполнении тестов. Используйте один из: [none, flat, tree, verbose]. Если выбрано «none», то отображаются только сводка и неудачные тесты. (по умолчанию: tree) |
| --details-theme <[ascii,unicode]>             | Выбор темы дерева детализации вывода при выполнении тестов. Используйте одну из: [ascii, unicode] (по умолчанию: unicode) |
| --class-path, --classpath, --cp <Путь: путь1:путь2:...> | Предоставление дополнительных записей пути к классам — например, для добавления движков и их зависимостей. Эту опцию можно повторить. |
| --reports-dir <Путь>                          | Включение вывода отчётов в указанный локальный каталог (будет создан, если его нет). |
| --scan-class-path, --scan-classpath [Путь: путь1:путь2:...] | Сканирование всех каталогов в пути к классам или явных корней пути к классам. Без аргументов сканируются только каталоги в системном пути к классам, а также дополнительные записи пути к классам, предоставленные через -cp (каталоги и JAR-файлы). Явные корни пути к классам, которых нет в пути к классам, будут проигнорированы. Эту опцию можно повторить. |
| -u, --select-uri <URI>                        | Выбор URI для обнаружения тестов. Эту опцию можно повторить. |
| -f, --select-file <Строка>                    | Выбор файла для обнаружения тестов. Эту опцию можно повторить. |
| -d, --select-directory <Строка>               | Выбор каталога для обнаружения тестов. Эту опцию можно повторить. |
| -p, --select-package <Строка>                 | Выбор пакета для обнаружения тестов. Эту опцию можно повторить. |
| -c, --select-class <Строка>                   | Выбор класса для обнаружения тестов. Эту опцию можно повторить. |
| -m, --select-method <Строка>                  | Выбор метода для обнаружения тестов. Эту опцию можно повторить. |
| -r, --select-resource <Строка>                | Выбор ресурса пути к классам для обнаружения тестов. Эту опцию можно повторить. |
| -n, --include-classname <Строка>              | Предоставление регулярного выражения для включения только классов, чьи полные имена совпадают. Чтобы избежать ненужной загрузки классов, шаблон по умолчанию включает только имена классов, которые заканчиваются на... **Конкретный пример см. в DisabledCondition (https://github.com/junit-team/junit5/tree/r5.0.2/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java) и @Disabled (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/Disabled.html) в исходном коде.**

#### 5.3.1. Условия отключения (условия приостановки)

Иногда полезно запускать тестовый набор без определённых условий. Например, вы можете захотеть запустить тесты, даже если они помечены как `@Disabled`, чтобы проверить, не нарушены ли они. Для этого просто предоставьте параметр конфигурации режима junit.jupiter.conditions.deactivate, который должен отключить (то есть указать условие. Не оценивать) текущий запуск теста. Этот режим можно предоставить как системное свойство JVM, как параметр конфигурации, передаваемый LauncherDiscoveryRequest в Launcher, или через файл конфигурации платформы JUnit (см. раздел «Параметры конфигурации» (http://junit.org/junit5/docs/current/user-guide/#running-tests-config-params) для получения дополнительной информации).

Например, чтобы отключить условия JUnit `@Disabled`, вы можете использовать следующие системные свойства для запуска JVM:

`-Djunit.jupiter.conditions.deactivate=org.junit.*DisabledCondition`

#### Синтаксис сопоставления шаблонов

Если режим junit.jupiter.conditions.deactivate состоит из одного звёздочного знака (*), все условия будут отключены. В противном случае этот режим будет использоваться для сопоставления с полностью определённым именем класса (FQCN) каждого зарегистрированного условия. Точки (.) в режиме будут соответствовать точкам (.) или знакам доллара ($) в FQCN. Каждая звёздочка (*) в режиме будет соответствовать одному или нескольким символам в FQCN. Все остальные символы в режиме будут сопоставляться с FQCN.

Пример:

*: Отключает все условия.
org.junit.*: Отключает все условия в базовом пакете org.junit и его подпакетах.
*.MyCondition: Отключает условия каждого класса, полное имя которого — MyCondition.
*System*: Отключает все условия, содержащие System в имени класса.
org.example.MyCondition: Отключает условие, полное имя класса которого — org.example.MyCondition.

### 5.4. Обработка после тестирования экземпляра

TestInstancePostProcessor (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/TestInstancePostProcessor.html) определяет API расширений для публикации тестовых экземпляров.

Типичные варианты использования включают внедрение зависимостей в тестовые экземпляры, вызов пользовательских методов инициализации в тестовых экземплярах и т. д.

Для конкретных примеров см. MockitoExtension (https://github.com/junit-team/junit5-samples/tree/r5.0.2/junit5-mockito-extension/src/main/java/com/example/mockito/MockitoExtension.java) и SpringExtension (https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java).

### 5.5. Анализ параметров

ParameterResolver (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/ParameterResolver.html) определяет API расширения для динамического анализа параметров времени выполнения.

Если конструктор теста или методы @Test, @TestFactory, @BeforeEach, @AfterEach, @BeforeAll или @AfterAll принимают параметр, то этот параметр должен быть проанализирован ParameterResolver во время выполнения. ParameterResolver может быть встроенным (см. TestInfoParameterResolver (https://github.com/junit-team/junit5/tree/r5.0.2/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java)) или зарегистрированным пользователем. Обычно параметры анализируются по имени, типу, аннотации или любой их комбинации. См. CustomTypeParameterResolver (https://github.com/junit-team/junit5/tree/r5.0.2/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java) и CustomAnnotationParameterResolver (https://github.com/junit-team/junit5/tree/r5.0.2/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java) для конкретных примеров.

### 5.6. Обратные вызовы жизненного цикла теста

Следующие интерфейсы определяют API для расширения тестов на различных этапах выполнения теста. Чтобы узнать больше о деталях, обратитесь к каждому интерфейсу в пакете org.junit.jupiter.api.extension (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/package-summary.html).

- BeforeAllCallback (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/BeforeAllCallback.html)
  - BeforeEachCallback (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/BeforeEachCallback.html)
    - BeforeTestExecutionCallback (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html)
    - AfterTestExecutionCallback (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/AfterTestExecutionCallback.html)
  - AfterEachCallback (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/AfterEachCallback.html)
- AfterAllCallback (http://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/AfterAllCallback.html)

| ![Внимание](info.png "Внимание") | *Реализация нескольких API расширений* <br/> Разработчики расширений могут выбрать реализацию любого количества этих интерфейсов в одном расширении. См. SpringExtension (https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java) для конкретного примера. | **Перед выполнением теста и после него можно добавить поведение, которое будет выполняться немедленно.** Для этого предназначены обратные вызовы BeforeTestExecutionCallback и AfterTestExecutionCallback. Они определены в API Extensions.

Эти обратные вызовы хорошо подходят для случаев, когда нужно измерить время (например, засечь его), отследить что-либо или выполнить подобные задачи. Если необходимо реализовать обратные вызовы @BeforeEach и @AfterEach, то следует использовать реализации BeforeEachCallback и AfterEachCallback.

**Пример использования этих обратных вызовов для измерения и записи времени выполнения тестового метода:**

TimingExtension реализует BeforeTestExecutionCallback и AfterTestExecutionCallback, чтобы записывать время выполнения тестов.

*Пример расширения, которое измеряет и записывает время выполнения тестовых методов:*

```java
import java.lang.reflect.Method;
import java.util.logging.Logger;

import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;

public class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {

    private static final Logger LOG = Logger.getLogger(TimingExtension.class.getName());

    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        getStore(context).put(context.getRequiredTestMethod(), System.currentTimeMillis());
    }

    @Override
    public void afterTestExecution(ExtensionContext context) throws Exception {
        Method testMethod = context.getRequiredTestMethod();
        long start = getStore(context).remove(testMethod, long.class);
        long duration = System.currentTimeMillis() - start;

        LOG.info(() -> String.format("Метод [%s] занял %s мс.", testMethod.getName(), duration));
    }

    private Store getStore(ExtensionContext context) {
        return context.getStore(Namespace.create(getClass(), context));
    }
}

Поскольку класс TimingExtensionTests регистрирует TimingExtension с помощью @ExtendWith, тесты этого класса будут иметь функцию таймера.

Пример использования TimingExtension:

@ExtendWith(TimingExtension.class)
class TimingExtensionTests {

    @Test
    void sleep20ms() throws Exception {
        Thread.sleep(20);
    }

    @Test
    void sleep50ms() throws Exception {
        Thread.sleep(50);
    }
}

Вот пример журнала, который генерируется при выполнении TimingExtensionTests:

INFO: Метод [sleep20ms] занял 24 мс. INFO: Метод [sleep50ms] занял 53 мс.

5.7. Обработка исключений

TestExecutionExceptionHandler определяет API расширений, которые обрабатывают исключения, возникающие во время выполнения теста.

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

Расширение для обработки исключений:

public class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler {

    @Override
    public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
            throws Throwable {

        if (throwable instanceof IOException) {
            return;
        }
        throw throwable;
    }
}

5.8. Предоставление контекста вызова для тестовых шаблонов

Методы @TestTemplate могут быть выполнены только после регистрации как минимум одного TestTemplateInvocationContextProvider. Каждый такой поставщик отвечает за предоставление потока экземпляров TestTemplateInvocationContext. Каждый контекст может указывать настраиваемое отображаемое имя и дополнительный список расширений, которые будут использоваться только для следующего вызова метода @TestTemplate.

Следующий пример показывает, как написать тестовый шаблон и зарегистрировать и реализовать TestTemplateInvocationContextProvider. annotation org.junit.jupiter.api.Test | user code of the actual test method | | 7 | interface org.junit.jupiter.api.extension.TestExecutionExceptionHandler | extension code for handling exceptions thrown during a test | | 8 | interface org.junit.jupiter.api.extension.AfterTestExecutionCallback | extension code executed immediately after test execution and its corresponding exception handlers | | 9 | annotation org.junit.jupiter.api.AfterEach | user code executed after each test is executed | | 10 | interface org.junit.jupiter.api.extension.AfterEachCallback | extension code executed after each test is executed | | 11 | annotation org.junit.jupiter.api.AfterAll | user code executed after all tests of the container are executed | | 12 | interface org.junit.jupiter.api.extension.AfterAllCallback | extension code executed after all tests of the container are executed |

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

6. Переход с JUnit 4

Хотя модель программирования и модель расширения JUnit Jupiter не поддерживают функции JUnit 4, такие как Rules и Runners, разработчики не должны обновлять все свои тесты, тестовые расширения и настраиваемые инфраструктуры для сборки тестов при переходе на JUnit Jupiter.

Вместо этого JUnit предоставляет плавный путь миграции через тестовый движок JUnit Vintage, который позволяет выполнять существующие тесты на основе JUnit 3 и JUnit 4 с использованием инфраструктуры JUnit Platform. Поскольку все классы и аннотации JUnit Jupiter находятся в новом пакете org.junit.jupiter, наличие пакетов JUnit 4 и JUnit Jupiter в пути к классам не вызовет конфликтов. Таким образом, безопасно поддерживать существующие тесты JUnit 4 вместе с тестами JUnit Jupiter. Кроме того, команда JUnit продолжит предоставлять поддержку и исправления ошибок для JUnit 4.x. У разработчиков есть достаточно времени, чтобы перейти на JUnit Jupiter по своему собственному графику.

6.1. Запуск тестов JUnit 4 на платформе JUnit

Просто убедитесь, что компонент junit-vintage-engine находится в вашем пути выполнения теста. В этом случае JUnit 3 и JUnit 4 тесты будут автоматически распознаны запускающим устройством JUnit платформы.

См. примеры проектов в репозитории junit5-samples GitHub для получения информации о том, как выполнить это с помощью Gradle и Maven.

6.2. Советы по переходу

При переносе существующих тестов JUnit 4 в JUnit Jupiter следует учитывать следующие моменты:

  • Аннотации находятся в пакете org.junit.jupiter.api.
  • Утверждения находятся в пакете org.junit.jupiter.api.Assertions.
  • Предположения находятся в пакете org.junit.jupiter.api.Assumptions.
  • @Before и @After больше не существуют; используйте @BeforeEach и @AfterEach вместо них.
  • @BeforeClass и @AfterClass больше не существуют; используйте @BeforeAll и @AfterAll вместо них.
  • @Ignore больше не существует; используйте @Disabled вместо него.
  • @Category больше не существует; используйте @Tag вместо него.
  • @RunWith больше не существует; замените его на @ExtendWith.
  • @Rule и @ClassRule больше не существуют; замените их на @ExtendWith; для получения информации об ограниченной поддержке правил см. следующий раздел.

6.3. Ограниченная поддержка правил JUnit 4

Как упоминалось ранее, JUnit Jupiter не поддерживает правила JUnit 4. Однако команда JUnit осознаёт, что многие организации, особенно крупные, могут иметь большие кодовые базы JUnit 4, использующие пользовательские правила. Чтобы обслуживать эти организации и обеспечить постепенный путь перехода, команда JUnit решила предоставить ограниченную поддержку правил JUnit 4 в JUnit Jupiter на основе адаптеров. Эта поддержка ограничивается теми правилами, семантика которых совместима с моделью расширения JUnit Jupiter (то есть не полностью изменяет общий поток выполнения теста).

Модуль junit-jupiter-migrationsupport JUnit Jupiter поддерживает следующие три типа правил, включая их подклассы:

  • org.junit.rules.ExternalResource (включая org.junit.rules.TemporaryFolder)
  • org.junit.rules.Verifier (включая org.junit.rules.ErrorCollector)
  • org.junit.rules.ExpectedException

Правила можно использовать с аннотациями уровня класса, которые поддерживают поля и методы. Используя эти классы уровня расширения в тестовом классе, можно сохранить Rule реализации без изменений в унаследованных кодовых базах, включая операторы импорта правил JUnit 4.

Этот ограниченный вид поддержки правил может быть запущен с помощью аннотаций уровня класса org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport. Эта аннотация является составной аннотацией, которая поддерживает все расширения миграции: VerifierSupport, ExternalResourceSupport и ExpectedExceptionSupport.

Однако, если вы планируете разработать новое расширение для JUnit 5, рекомендуется использовать новую модель расширений JUnit Jupiter, а не основанную на правилах модель JUnit 4.

警告 Поддержка правил JUnit 4 в настоящее время является экспериментальной функцией. См. таблицу экспериментальных API (Experimental APIs) для получения дополнительной информации.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/liushide-junit5_cn_doc.git
git@api.gitlife.ru:oschina-mirror/liushide-junit5_cn_doc.git
oschina-mirror
liushide-junit5_cn_doc
liushide-junit5_cn_doc
master