Переклад українською - Арсеній Чеботарьов - Київ 2019
Ціль цього документу є запровадити повну документацію для програмістів, що пишуть тести, авторів розширень, та авторів двигуна, так само, як для постачальників інструментів побудови та IDE.
Цей документ також доступний як PDF завантаження.
Переклади
Цей документ також доступний на Спрощеній китайській та Японській мовах. |
На відміну від попередніх версій JUnit, JUnit 5 складається з різних модулів від трьох різних суб-проектів.
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit
Platform служить як основа для запуску
тестових фреймворків на JVM. Він також
визначає TestEngine
API
для розробки платформ тестування, що робить на цій платформі.
Більше того, платформа провадить Консольний
запускач для запуску платформи з командного рядка, та
побудови плагінів для Gradle
та Maven
, так само, як і базований
на JUnit 4 Runner для виконання любого TestEngine
на платформі.
JUnit
Jupiter є комбінацією нової програмної
моделі та моделі
розширення для написання тестів та розширень
на JUnit 5. Субпроект Jupiter провадить TestEngine
для виконання базованих на Jupiter тестів на платформі.
JUnit
Vintage провадить TestEngine
для виконання тестів JUnit 3 та JUnit 4 на платформі.
JUnit 5 потребує Java 8 (або вище) під час виконання. Однак ви все ще можете тестувати код, що був скомпільований з попередніми версіями JDK.
Запитуйте питання відносно JUnit 5 на Stack Overflow або в чаті з нами на Gitter.
Артифакти для фінального релізу та майлстонів викладаються на Maven Central.
Артифакти снепшотів викладаються на репозитарій снепшотів Sonatype під /org/junit.
Групове
ID: org.junit.platform
Версія: 1.3.2
Артифактні ID:
junit-platform-commons
Загальна внутрішня бібліотека/утіліти of JUnit. Ці утіліти призначені суцільно для використання в самому фреймворці JUnit. Любе використання зовнішніми сторонами не підтримується. Використовуйте на власний розсуд!
junit-platform-console
Підтримка для пошуку та виконання тестів на JUnit Platform з консолі. Дивіться Консольний запускач щодо деталей.
junit-platform-console-standalone
Виконуваний JAR з усіма включеними залежностями, запроваджений на Maven Central в директорії junit-platform-console-standalone. Дивіться Консольний запускач щодо деталей.
junit-platform-engine
Публічний API для рушиїв тестування. Дивіться Підключення вашого власного тестового рушія щодо деталей.
junit-platform-launcher
Публічне API для конфігурації та запуску тестових планів — типово використовується IDE та інструментами побудови. Дивіться JUnit Platform Launcher API щодо деталей.
junit-platform-runner
Виконувач для запуску тестів та тестових сюїт на JUnit Platform в оточенні JUnit 4. Дивіться Використання JUnit 4 для виконання JUnit Platform щодо деталей.
junit-platform-suite-api
Анотації
для конфігурації тестових сюїт на JUnit Platform.
Підтримується JUnitPlatform
runner і можливо реалізаціямиTestEngine
третіх сторін.
junit-platform-surefire-provider
Підтримує пошук та виконання тестів на JUnit Platform з використанням Maven Surefire.
Групове
ID: org.junit.jupiter
Версія: 5.3.2
Артифактні ID:
junit-jupiter-api
JUnit Jupiter API для написання тестів та розширень.
junit-jupiter-engine
Реалізація тестового рушія JUnit Jupiter, єдине що потрібне під чс виконання.
junit-jupiter-params
Підтримка для параметризованих тестів в JUnit Jupiter.
junit-jupiter-migrationsupport
Підтримка міграції від JUnit 4 до JUnit Jupiter, єдине що потрібне для виконання обраних тестів JUnit 4.
Груповий
ID: org.junit.vintage
Версія: 5.3.2
Артифактне ID:
junit-vintage-engine
Реалізація тестового рушія JUnit Vintage, що дозволяє виконувати вінтажні тести JUnit, тобто тесті, написаних в стилі JUnit 3 або JUnit 4, на новій платформі JUnit Platform.
Bill of Materials POM, запроваджений під наступними координатами Maven може бути використаний для спрощення керування залежностями, коли посилаєтесь на декілька з приведених нижче артифактів, використовуючи Maven або Gradle.
Групове
ID: org.junit
Артифактне
ID: junit-bom
Версія: 5.3.2
Всі з артифактів вище мають свої залежності в своїх опублікованих Maven POM на наступний @API Guardian JAR.
Групове
ID: org.apiguardian
Артифактне
ID: apiguardian-api
Версія: 1.0.0
На додаток, більшість з артифактів вище мають пряму або транзитивну залежність на наступний OpenTest4J JAR.
Групове
ID: org.opentest4j
Артифактне
ID: opentest4j
Версія: 1.1.1
Репозитарій
junit5-samples
містить колекцію прикладів проектів на базі JUnit
Jupiter та JUnit Vintage. Ви знайдете відповідні скрипти побудови(build.gradle
, pom.xml
,
etc.) в проекті нижче.
Для
Gradle та Java, перевірте проект junit5-jupiter-starter-gradle
.
Для
Gradle та Kotlin, перевірте проект junit5-jupiter-starter-gradle-kotlin
.
Для
Gradle та Groovy, перевірте проект junit5-jupiter-starter-gradle-groovy
.
Для
Maven, перевірте проект junit5-jupiter-starter-maven
.
Для
Ant, первірте проект junit5-jupiter-starter-ant
.
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class FirstJUnit5Tests {
@Test
void myFirstTest() {
assertEquals(2, 1 + 1);
}
}
JUnit Jupiter підтримує наступні анотації для конфігурації тестів та розширення фреймворку.
Всі
головні анотації розташовані в пакунку org.junit.jupiter.api
в модулі junit-jupiter-api
.
Анотація | Опис |
---|---|
|
Вказує,
що метод є методом тестування. На відміну анотації |
|
Вказує, що метод є параметризованим тестом. Такі методи наслідувані, за винятком, коли вони перекриті. |
|
Вказує, що метод є шаблоном теста для повторюваниз тестів. Такі методи наслідувані, за винятком, коли вони перекриті. |
|
Вказує, що метод є фабрикою тестів для динамічних тестів. Такі методи наслідувані, за винятком, коли вони перекриті. |
|
Використовується для конфігурації життєвого циклу примірника тесту для анотованого тестового класу. Такі методи наслідувані. |
|
Вказує, що метод є шаблоном для тестових випадках, розробленим бути викликаним декілька разів, в залежності від числа виклику контекстів, що повертаються від зареєстрованих провайдерів. Такі методи наслідувані, за винятком, коли вони перекриті. |
|
Декларує власне дисплейне ім'я для тестового класу або тестового методу. Такі методи наслідувані. |
|
Вказує,
що анотований метод повинен бути викликаний перед
кожним
методом |
|
Вказує,
що анотований метод повинен буде викликаний після кожного
метода |
|
Вказує,
що анотований метод повинен бути викликаний перед
всіма
методами
|
|
Вказує,
що анотований метод повинен бути викликаний після
всіх
методів |
|
Вказує,
що анатований клас є вкладений, нестатичний тестовий клас.
Методи |
|
Використовується для декларації тегів для фільтруючих тестів, на рівні або класа, або метода; аналогічно до груп тестів в TestNG або Категорій в JUnit 4. Такі анотації наслідувані на рівні класу, але не на рівні методів. |
|
Використовується
для відключення тестового класу або тестового
методу; аналогічно до JUnit 4 |
|
Використовується для реєстрації власних розширень. Такі анотації наслідувані. |
Методі,
анотовані за допомогою @Test
,
@TestTemplate
,
@RepeatedTest
,
@BeforeAll
,
@AfterAll
,
@BeforeEach
,
або анотацій @AfterEach
не повинні повертати значення.
Деякі анотації досі можуть бути в стані експериментальних. Звіряйтесь з таблицею Consult the table in Experimental APIs for details. |
Анотации JUnit Jupiter можуть бути використані як мета-анотації. Це означає, що ви можете визначити вашу власну композитну анотацію, що буде автоматично наслідувати семантику своїх мета-анотацій.
Наприклад,
замість копіювання та передачі @Tag("fast")
по всій вашій кодовій базі (дивіться Тегі
та фільтрація), ви можете створити власну композитну
анотацію на ім'я @Fast
наступним чином. Потім @Fast
може бути використана як заміна для @Tag("fast")
.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Tag;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}
Тестовий
метод є любим методом примірника, що напряму або
мета-анотований @Test
,
@RepeatedTest
,
@ParameterizedTest
,
@TestFactory
,
або @TestTemplate
.
Тестовий клас є любий клас вищого рівня або клас
статичний член, що містить хоча б один тестовий метод.
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class StandardTests {
@BeforeAll
static void initAll() {
}
@BeforeEach
void init() {
}
@Test
void succeedingTest() {
}
@Test
void failingTest() {
fail("a failing test");
}
@Test
@Disabled("for demonstration purposes")
void skippedTest() {
// not executed
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
}
Ні
тестовим класам, ані тестовим методам не треба бути public . |
Тестові класи та тестові методи можуть декларувати власні дісплейні імена — з проміжками, спеціальними символами, та навіть емоджи — що може відображуватись виконавцями тестів та в тестових звітах.
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("A special test case")
class DisplayNameDemo {
@Test
@DisplayName("Custom test name containing spaces")
void testWithDisplayNameContainingSpaces() {
}
@Test
@DisplayName("╯°□°)╯")
void testWithDisplayNameContainingSpecialCharacters() {
}
@Test
@DisplayName("😱")
void testWithDisplayNameContainingEmoji() {
}
}
JUnit
Jupiter іде разом з багатьма методами тверджень, що мав JUnit 4,
та додає нові, що гарно вкладаються привикористанні разом з
лямбдами Java 8. Всі твердження JUnit Jupiter є static
методи в класі org.junit.jupiter.api.Assertions
.
import static java.time.Duration.ofMillis; import static java.time.Duration.ofMinutes; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeout; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class AssertionsDemo { @Test void standardAssertions() { assertEquals(2, 2); assertEquals(4, 4, "Опціональне повідомлення твердження тепер іде останнім параметром."); assertTrue('a' < 'b', () -> "Повідомлення тверджень можуть бути обчислені ліниво" + "щоб уникнути побудови складних повідомлень без потреби."); } @Test void groupedAssertions() { // В згрупованих твердженнях
виконуються
всі твердження, //
та любі
збої будуть прозвітовані разом. assertAll("person", () -> assertEquals("John", person.getFirstName()), () -> assertEquals("Doe", person.getLastName()) ); } @Test void dependentAssertions() { // В блоці коду, якщо твердження схибить, залишок коду // в тому ж блоці буде пропущено. assertAll("properties", () -> { String firstName = person.getFirstName(); assertNotNull(firstName); // Виконується, тільки якщо попереднє твердження валідне. assertAll("first name", () -> assertTrue(firstName.startsWith("J")), () -> assertTrue(firstName.endsWith("n")) ); }, () -> { // Згруповане твердження, так що обробляється незалежно // від результатів тверджень першого імені. String lastName = person.getLastName(); assertNotNull(lastName); // Виконується тільки якщо попереднє твердження валідне. assertAll("last name", () -> assertTrue(lastName.startsWith("D")), () -> assertTrue(lastName.endsWith("e")) ); } ); } @Test void exceptionTesting() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { throw new IllegalArgumentException("a message"); }); assertEquals("a message", exception.getMessage()); } @Test void timeoutNotExceeded() { // Наступне твердження спрацює. assertTimeout(ofMinutes(2), () -> { // Виконати завдання, що займе меньше ніж 2 хвилини. }); } @Test void timeoutNotExceededWithResult() { // Наступне твердження буде успішним, та поверне наданий об'єкт. String actualResult = assertTimeout(ofMinutes(2), () -> { return "a result"; }); assertEquals("a result", actualResult); } @Test void timeoutNotExceededWithMethod() { // Наступне твердження викликає метод, та поверає об'єкт. String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting); assertEquals("Hello, World!", actualGreeting); } @Test void timeoutExceeded() { // Наступне твердження схибить з повідомлення помилки, подібним до наступного: // виконання перевищило таймаут 10 ms на 91 ms assertTimeout(ofMillis(10), () -> { // Симулювати завдання, що займе більше 10 ms. Thread.sleep(100); }); } @Test void timeoutExceededWithPreemptiveTermination() { // Наступне твердження схибить з повідомленням помилки, подібним до наступного: // execution timed out after 10 ms assertTimeoutPreemptively(ofMillis(10), () -> { // Симулює завдення, що займає більше ніж 10 ms. Thread.sleep(100); }); } private static String greeting() { return "Hello, World!"; } }
JUnit
Jupiter також іде з декільками методами, що гарно пристосовані для
використання разом з Kotlin.
Всі твердження JUnit Jupiter Kotlin є функціями вищого рівня в
пакунку org.junit.jupiter.api
.
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.assertThrows
class AssertionsKotlinDemo {
@Test
fun `grouped assertions`() {
assertAll("person",
{ assertEquals("John", person.firstName) },
{ assertEquals("Doe", person.lastName) }
)
}
@Test
fun `exception testing`() {
val exception = assertThrows<IllegalArgumentException> ("Should throw an exception") {
throw IllegalArgumentException("a message")
}
assertEquals("a message", exception.message)
}
@Test
fun `assertions from a stream`() {
assertAll(
"people with name starting with J",
people
.stream()
.map {
// This mapping returns Stream<() -> Unit>
{ assertTrue(it.firstName.startsWith("J")) }
}
)
}
@Test
fun `assertions from a collection`() {
assertAll(
"people with last name of Doe",
people.map { { assertEquals("Doe", it.lastName) } }
)
}
}
Навіть якщо можливості тверджень, запроваджених JUnit Jupiter достатні для багатьох тестових сценаріїв, є випадки, коли потрібні та бажані більшіе потужності та додаткова функціональність, як використання матчерів. В таких випадках команда JUnit рекомендує використання бібліотек третіх сторін, таких, як AssertJ, Hamcrest, Truth, тощо. Таким чином розробники вільні використовувати бібліотеку тверджень свого вибору.
Наприклад,
комбінаця матчерів
та гнучкого API можуть бути використані, щоб зробити
твердження більш описовими та читаємими. Однак, клас JUnit
Jupiter’s org.junit.jupiter.api.Assertions
не провадить метод assertThat()
,
як такий, що можна знайти в класі JUnit 4’s org.junit.Assert
,
що сприймає Hamcrest Matcher
.
Замість цього розробники заохоочуються використовувати вбудовану
підтримку для матчерів, що провадяться бібліотеками тверджень
третіх сторін.
Наступний
приклад демонструє, як використовувати підтримку assertThat()
від Hamcrest в тесті JUnit Jupiter. Доки бібліотека
Hamcrest буде додана до classpath, ви можете статично
імпортувати методи, такі, як 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
.
JUnit
Jupiter іде разом з підмножиною методів припущень, що провадить
JUnit 4, та додає декілька, що гарно пасують для використання
разом з лямбдами Java 8. Всі припущення JUnit Jupiter є статичними
методами в класі org.junit.jupiter.api.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"))); // залишок тесту } @Test void testOnlyOnDeveloperWorkstation() { assumeTrue("DEV".equals(System.getenv("ENV")), () -> "Aborting test: not on developer workstation"); //
залишок тесту
} @Test void testInAllEnvironments() { assumingThat("CI".equals(System.getenv("ENV")), () -> { // виконувати ці твердження тільки на CI сервері assertEquals(2, 2); }); // виконувати ці твердження у всіх оточеннях assertEquals("a string", "a string"); } }
Цілі
класи тестів або окремі методи тестів можуть бути відключені
через анотацію @Disabled
,
через одну з анотацій, обговорених в Умовне
виконання тестів, або через власну умову ExecutionCondition
.
Ось
як @Disabled
відключає тестовий клас.
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled
class DisabledClassDemo {
@Test
void testWillBeSkipped() {
}
}
Та
ось тестовий клас, що містить метод з@Disabled
.
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class DisabledTestsDemo {
@Disabled
@Test
void testWillBeSkipped() {
}
@Test
void testWillBeExecuted() {
}
}
Розширення
ExecutionCondition
API в JUnit Jupiter дозволяє розробникам вмикати або
вимикати контейнер або тест, базуючись на певних умовах,
програмно. Простіший приклад такої умови є вбудований DisabledCondition
,
що підтримує анотацію @Disabled
(дивіться Відключення
тестів). На додаток до @Disabled
,
JUnit Jupiter також підтримує декілька інших, базованих на
анотаціях умов в пакунку org.junit.jupiter.api.condition,
що дозволяє розробникам вмикати та вимикати контейнери та тести
декларативно. Дивіться наступні розділи щодо деталей.
Композитні
анотації
Зауважте,
що всі з умовних анотацій, перелічених в
наступних розділів, також можуть бути використані як
мета-анотації для створення власних композитних
анотацій. Наприклад, анотація |
Кожна
з умовних анотацій, перелічених в наступних
розділах, може бути декларована тільки один раз на
окремому тестововму інтерфейсі, тестовому класі, або
тестовому методі. Якщо умовна анотація явно присутня,
неявно присутня, або мета-присутня декілька разів для
окремого елементу, тільки перша така анотація, знайдена
JUnit, буде використана; любі додаткові декларації
будуть тихо ігноровані. Зауважте, однак, що кожна умовна
анотація може бути використана в поєднанні з іншими
умовними анотаціями в пакунку |
Контейнер
або тест може бути вімкнений або вимкнений
на певній операційній системі через анотації @EnabledOnOs
та @DisabledOnOs
.
@Test
@EnabledOnOs(MAC)
void onlyOnMacOs() {
// ...
}
@TestOnMac
void testOnMac() {
// ...
}
@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
// ...
}
@Test
@DisabledOnOs(WINDOWS)
void notOnWindows() {
// ...
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@EnabledOnOs(MAC)
@interface TestOnMac {
}
Контейнер
або тест можуть бути вімкнені або вимкнені
на певних версіях Java Runtime Environment (JRE) через анотації
@EnabledOnJre
та @DisabledOnJre
.
@Test
@EnabledOnJre(JAVA_8)
void onlyOnJava8() {
// ...
}
@Test
@EnabledOnJre({ JAVA_9, JAVA_10 })
void onJava9Or10() {
// ...
}
@Test
@DisabledOnJre(JAVA_9)
void notOnJava9() {
// ...
}
Контейнер
або тест можуть бути вімкнені або вимкнені
на основі значення системної властивостіі JVM named
через
анотації @EnabledIfSystemProperty
та @DisabledIfSystemProperty
.
Значення, що надходить через атрибут matches
,
буде інтерпретоване як регулярний вираз.
@Test
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
void onlyOn64BitArchitectures() {
// ...
}
@Test
@DisabledIfSystemProperty(named = "ci-server", matches = "true")
void notOnCiServer() {
// ...
}
Контейнер
або тест можуть бути вімкнені або вимикнені
на основі значення змінної оточення named
,
отриманої від операційної системи, через анотації @EnabledIfEnvironmentVariable
та @DisabledIfEnvironmentVariable
.
Значення, надане через атрибут matches
,
буде інтерпретоване як регулярний вираз.
@Test
@EnabledIfEnvironmentVariable(named = "ENV", matches = "staging-server")
void onlyOnStagingServer() {
// ...
}
@Test
@DisabledIfEnvironmentVariable(named = "ENV", matches = ".*development.*")
void notOnDeveloperWorkstation() {
// ...
}
JUnit
Jupiter дозволяє вмикати або вимикати
контейнер або тест в залежності від обчислення скрипта,
сконфігурованого через анотації @EnabledIf
або @DisabledIf
.
Скрипти можуть бути написані на JavaScript, Groovy, або любій
скриптовій мові, для якого є підтримка для Java Scripting
API, визначеного в JSR 223.
Умовне
виконання тестів через @EnabledIf
та @DisabledIf
наразі є експериментальною можливістюЗвертайтесь
до таблиці Експериментальні
API щодо деталей. |
Якщо логіка вашого скрипта залежить тільки від поточною операційної системи, поточної версії Java Runtime Environment, певної властивості системи JVM, або від змінної оточення, вам слід розглянути використання вбудованих анотацій, признацених для ціх цілей. Дивіться розділи в цій главі для подальних деталей. Дивіться попередні розділи цієї глави для подальших деталей. |
Якщо
ви захопите себе за постійним використанням того самого
скрипта в умовах, розгляньте можливість написання
розширення ExecutionCondition,
щоб реалізувати умову в швидшій, типо-безпечній та більш
керованій манері. |
@Test // Статичний вираз JavaScript.
@EnabledIf("2 * 3 == 6")
void willBeExecuted() {
// ...
}
@RepeatedTest(10) // Динамічний вираз JavaScript.
@DisabledIf("Math.random() < 0.314159")
void mightNotBeExecuted() {
// ...
}
@Test // Регулярний вираз перевірки прив'язаної системної властивості.
@DisabledIf("/32/.test(systemProperty.get('os.arch'))")
void disabledOn32BitArchitectures() {
assertFalse(System.getProperty("os.arch").contains("32"));
}
@Test
@EnabledIf("'CI' == systemEnvironment.get('ENV')")
void onlyOnCiServer() {
assertTrue("CI".equals(System.getenv("ENV")));
}
@Test // Багаторядний скрипт, власне ім'я рушія та власний розсуд.
@EnabledIf(value = {
"load('nashorn:mozilla_compat.js')",
"importPackage(java.time)",
"",
"var today = LocalDate.now()",
"var tomorrow = today.plusDays(1)",
"tomorrow.isAfter(today)"
},
engine = "nashorn",
reason = "Self-fulfilling: {result}")
void theDayAfterTomorrow() {
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
assertTrue(tomorrow.isAfter(today));
}
Наступні
імена прив'язані до контексту кожного скрипта, і, таким чином,
можуть використовуватись в скрипті. accessor
провадить до мапа-подібної структури через простий метод String
get(String name)
.
Ім'я | Тип | Опис |
---|---|---|
|
accessor |
Доступ до змінної оточення операційної системи. |
|
accessor |
Доступ до системної властивості JVM. |
|
accessor |
Доступ до параметрів конфігурації. |
|
|
Дисплейне ім'я тесту або контейнеру. |
|
|
Всі теги, доступні для теста або контейнера. |
|
|
Унікальний ID теста або контейнера. |
Тестові
класи та методи можуть бути відмічені за допомогою анотації
@Tag
.
Ці теги можуть бути пізніше використані для фільтрації пошуку
та виконання тестів.
Тег
на має бути null
або порожнім.
Усічений тег не має містити проміжків.
Усічений тег не має містити контрольні символи ISO.
Усічений тег не має містити любі з наступних зарезервованих символів.
,
:
кома
(
:
ліва дужка
)
:
права
дужка
&
:
амперсанд
|
:
вертикальна
риска
!
:
знак
наголосу
В контексті вище, усічений означає, що проміжки з обох сторін були видалені. |
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@Tag("fast")
@Tag("model")
class TaggingDemo {
@Test
@Tag("taxes")
void testingTaxCalculation() {
}
}
Щоб дозволити окремим тестовим методам виконуватись в ізоляції, та щоб уникнути неочікуваних побічних ефектів через змінний стан примірника тесту, JUnit створює новий примірник для кожного тестового класу, перед виконанням кожного тестового метода (дивіться Тестові класи та методи). Цей "по-методний" життєвий цикл примірника тесту є поведінкою по замовчанню в JUnit Jupiter, та аналогічно до всіх попередніх версій JUnit.
Будь
ласка, зауважте, що тестові класи все ще будуть
створюватись, навіть якщо тестовий метод відключений через
умову
(@Disabled , @DisabledOnOs тощо
), також коли активний "по-методний" режим життевого циклу. |
Якщо
замість цього ви бажаєте, щоб JUnit Jupiter виконував всі тестові
методи на тому самому примірникові, просто анотуйте ваш клас за
допомогою@TestInstance(Lifecycle.PER_CLASS)
.
Коли використовується цей режим, новий примірник буде створюватись
один раз для кожного класу. Таким чином, якщо ваші тестові методи
покладаються на стан, збережений в змінних примірника, ви можете
потребувати скидати цей стан в методах @BeforeEach
або @AfterEach
.
"По-класовий"
режим має додаткові вигоди, порівняно з "по-методним" режимом.
Зокрема, "по-класовий" режим дозволяє декларувати @BeforeAll
та @AfterAll
на нестатичних методах, так само, як і на методах інтерфейсів default
.
Таким чином, "по-класовий" режим також робить можливим
використовувати методи @BeforeAll
та @AfterAll
у @Nested
тестових
класах.
Якщо
ви створюєте тести з використанням програмної мови Kotlin, ви
також можете знайти легшим реалізувати методи @BeforeAll
та
@AfterAll
,
перемикнувшись на рижм життєвого циклу "по-класовий".
Якщо
тестовий клас або тестовий інтерфейс не анотований за допомогою
@TestInstance
,
JUnit Jupiter буде використовувати режим життєвого циклу по
замовчанню. Стандартний метод по замовчанню є PER_METHOD
;
однак можливо змінити це замовчення для всього
тестового плану. Щоб змінити цей режим життєвого циклу по
замовчанню, просто встановіть параметр конфігурації
junit.jupiter.testinstance.lifecycle.default
в ім'я константи конфігурації, визначену в TestInstance.Lifecycle
,
ігноруючи реєстр. Це може надаватись як системна
властивість JVM, як параметр конфігурації
в LauncherDiscoveryRequest
,
що передається до Launcher
,
або через файл конфігурації JUnit Platform (дивіться Параметри
конфігурації щодо деталей).
Наприклад,
щоб встановити режим життєвого циклу примірника тесту в Lifecycle.PER_CLASS
,
ви можете запустити вашу JVM з наступною системною властивістю.
-Djunit.jupiter.testinstance.lifecycle.default=per_class
Зауважте, однак, що встановлення цього режиму по замовчанню через файл конфігурації JUnit Platform є більш надійним рішенням, оскільки файл конігурації може перевірятись системою контроля версій, разом з вашим проектом, і таким чином може бути використаний без IDE та ваших інструментів побудови.
Щоб
встановити режим життєвого примірника в Lifecycle.PER_CLASS
через файл конфігурації JUnit Platform, створіть фалй
junit-platform.properties
в корені шляху до класів (src/test/resources
)
з наступним вмістом.
junit.jupiter.testinstance.lifecycle.default
= per_class
Зміна режиму по замовчанню життєвого циклу примірника тесту може призвести до непередбачуваних результатів, та крихких побудов, якщо не використовувати його послідовно. Наприклад, якщо побудова конфігурує "по-класову" семантику по замовчанню, але тести в IDE виконуються з використанням семантики "по-методно", може виявитись складним полагодити помилки, що трапляються на сервері побудови. Таким чином, рекомендовано змінювати замовчання в файлі конфігурації JUnit Platform, замість системної властивості JVM. |
Вкладені тести дають дописувачеві тестів можливості виражати відношення між різними групами тестів. Ось надуманий приклад.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.EmptyStackException;
import java.util.Stack;
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());
}
}
}
}
Тільки
не-статичні вкладені класи (внутрішні
класи ) можуть служити як @Nested
тестові класи. Вкладання може бути довільно
глибоким, і ці внутрішні класи розглядаються як повні члени
родини тестових класів, з одним виключенням: методи @BeforeAll
та @AfterAll
не роблять по замовчанню. Причина в тому,
що Java не дозволяє static
члени у внутрішніх класах. Однак, це обомеження
може бути обійдене, анотуючи тестовий клас @Nested
за допомогою @TestInstance(Lifecycle.PER_CLASS) (дивіться
Життєвий
цикл тестових класів). |
В
усіх попередніх версіях JUnit, тестові конструктори або методи не
могли мати параметрів (принаймі не в реалізаціях зі стандартним Runner
).
Одна
велика зміна в JUnit Jupiter, що обоє, тестові конструктори та
методи тепер можуть мати параметри. Це надає кращу гнучкість, та
дозволяє Dependency
Injection для конструкторів та методів.
ParameterResolver
визначає API для тестових розширень, що бажають динамічно
розрішувати параметри від час виконання. Якщо тестовий
конструктор або метод @Test
, @TestFactory
, @BeforeEach
, @AfterEach
, @BeforeAll
,
або @AfterAll
приймає параметр, параметр має бути розрішений від час
виконання, через зареєстрований ParameterResolver
.
Наразі є три вбудовані ресолвери, що зареєстровані автоматично.
TestInfoParameterResolver
:
якщо параметр метода має тип TestInfo
,
TestInfoParameterResolver
буде надавати примірник TestInfo
,
відповідно до поточного тесту, як значення цього параметру.
TestInfo
може потім використовуватись для отримання інформації щодо
поточного тесту, такої, як дисплейне ім'я тесту, тестовий
клас, тестовий метод, або асоційовані теги. Дисплейне ім'я
це або технічне ім'я, таке, як ім'я тестованого класу і
тестованого методу, або власне ім'я, сконфігуроване через @DisplayName
.
TestInfo
діє як підміна для правила TestName
в JUnit 4. Наступне демонструє, як зробити ін'єкцію
TestInfo
в тестовий конструктор, метод @BeforeEach
,
та метод @Test
.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
@DisplayName("TestInfo Demo")
class TestInfoDemo {
TestInfoDemo(TestInfo testInfo) {
assertEquals("TestInfo Demo", testInfo.getDisplayName());
}
@BeforeEach
void init(TestInfo testInfo) {
String displayName = testInfo.getDisplayName();
assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
}
@Test
@DisplayName("TEST 1")
@Tag("my-tag")
void test1(TestInfo testInfo) {
assertEquals("TEST 1", testInfo.getDisplayName());
assertTrue(testInfo.getTags().contains("my-tag"));
}
@Test
void test2() {
}
}
RepetitionInfoParameterResolver
:
якщо параметр метода в методі @RepeatedTest
, @BeforeEach
,
або @AfterEach
має
тип RepetitionInfo
,
RepetitionInfoParameterResolver
буде постачати примірник RepetitionInfo
. RepetitionInfo
потім може використовуватись для отримання інформації щодо
поточного повторення, та загального числа повторів для
відповідного @RepeatedTest
.
Зауважте, однак, що RepetitionInfoParameterResolver
не зареєстрований за межами контексту @RepeatedTest
.
Дивіться Приклади
повторюваних тестів.
TestReporterParameterResolver
:
якщо параметр метода має тип TestReporter
,
TestReporterParameterResolver
буде постачати примірник TestReporter
.
TestReporter
може бути використаний для публікації додаткових даних щодо
поточного виконання тесту. Дані можуть бути спожиті через
TestExecutionListener.reportingEntryPublished()
,
і, таким чином, показані в IDE або вставлені в
звіти.
JUnit
Jupiter ви повинні використовувати TestReporter
,
де ви друкували інформацію до stdout
або stderr
в JUnit 4. Використання @RunWith(JUnitPlatform.class)
все одне буде надсилати всі повідомлення звіту до stdout
.
class TestReporterDemo {
@Test
void reportSingleValue(TestReporter testReporter) {
testReporter.publishEntry("a status message");
}
@Test
void reportKeyValuePair(TestReporter testReporter) {
testReporter.publishEntry("a key", "a value");
}
@Test
void reportMultipleKeyValuePairs(TestReporter testReporter) {
testReporter.publishEntry(
Map.of(
"user name", "dk38",
"award year", "1974"
));
}
}
Інші
ресолвери параметрів мають бути підключені явно, через
реєстрацію відповідного розширення
через @ExtendWith . |
Перевірте
RandomParametersExtension
в якості приклада власного ParameterResolver
.
Хоча не призначено бути готовим для примислового використання, це
демонструє простоту та виразність обох, моделі розширень, та
процесу розрішення параметрів. MyRandomParametersTest
демонструє, як ін'єктувати значення в методи
@Test
.
@ExtendWith(RandomParametersExtension.class)
class MyRandomParametersTest {
@Test
void injectsInteger(@Random int i, @Random int j) {
assertNotEquals(i, j);
}
@Test
void injectsDouble(@Random double d) {
assertEquals(0.0, d, 1.0);
}
}
Для
випадків з реального життя, перевірте наш джерельний код щодоMockitoExtension
та SpringExtension
.
JUnit
Jupiter дозволяє декларації @Test
,
@RepeatedTest
,
@ParameterizedTest
,
@TestFactory
,
@TestTemplate
,
@BeforeEach
,
та @AfterEach
на default
методах інтерфейсів. @BeforeAll
та @AfterAll
може бути або деклароване на static
методах в тестових інтерфейсах, або на методах default
інтерфейсу, якщо тестовий інтерфейс або
тестовий клас анотований з допомогою @TestInstance(Lifecycle.PER_CLASS)
(дивіться
Життєвий
цикл примірника тесту). Ось деякі приклади.
@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.
@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 returnsPositiveNumberWhenComparedToSmallerValue() {
T value = createValue();
T smallerValue = createSmallerValue();
assertTrue(value.compareTo(smallerValue) > 0);
}
@Test
default void returnsNegativeNumberWhenComparedToLargerValue() {
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";
}
}
Тести вище призначені тільки для прикладів, і, таким чином, не є закінченими. |
JUnit
Jupiter провадить здатність повторювати тест вказане число разів,
просто анотуючи метод за допомогою @RepeatedTest
,
та вказавши загальне число бажаних повторень. Кожний
виклик повторюваного тесту поводиться як виконання звичайного
метода @Test
,
з повною підтримкою того самого життєвого циклу, зі зворотніми
викликами та розширеннями.
Наступний
приклад демонструє, як декларувати тест з назвою repeatedTest()
,
що буде автоматично повторюватись 10 раз.
@RepeatedTest(10)
void repeatedTest() {
// ...
}
На
додаток до вказаної кількості повторів, може бути сконфігуроване
дисплейне ім'я для кожного повтору, через атрибутname
анотації @RepeatedTest
.
Більше того, дисплейне ім'я може бути шаблоном, що складається з
комбінації статичного тексту, та динамічних замінників. Наразі
підтримуються наступні замінники.
{displayName}
:
дисплейне ім'я методу @RepeatedTest
{currentRepetition}
:
поточний лічильник повторів
{totalRepetitions}
:
загальне число повторів
Дисплейне
ім'я по замовчанню для даного повтору генерується, базуючись на
наступному шаблоні: "repetition
{currentRepetition} of {totalRepetitions}"
. Таким чином,
дисплейні імена для окремих повторів в попередньому прикладі repeatedTest()
повинно бути: repetition
1 of 10
, repetition
2 of 10
, etc. Якщо ви бажаєте дисплейне ім'я метода @RepeatedTest
включити в ім'я кожного повтору, ви можете визначити свій
сласний шаблон, або використати передвизначений шаблон
RepeatedTest.LONG_DISPLAY_NAME
.
Останній еквівалентний до "{displayName}
:: repetition {currentRepetition} of {totalRepetitions}",
що
дає дисплейні імена для окремих повторів, як repeatedTest()
:: repetition 1 of 10
, repeatedTest()
:: repetition 2 of 10
, etc.
Щоб
отримати інформацію щодо поточного повтору та загального числа
повторів програмно, розробник може обрати мати примірник RepetitionInfo
ін'єктований в метод @RepeatedTest
, @BeforeEach
,
або @AfterEach
.
Клас
RepeatedTestsDemo
в кінці цього розділу демонструє деякі приклади повторюваних
тестів.
Метод
repeatedTest()
є ідентичним до приклада з попереднього розділу; деrepeatedTestWithRepetitionInfo()
демонструє, як мати примірник RepetitionInfo
ін'єктованим в тест, щоб мати загальне число повторів для
поточного повторюваного тесту.
Наступні
два методи демонструють, як включити власне @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
, etc.
Оскільки
метод beforeEach()
анотований за допомогою @BeforeEach
,
він буде виконаний перед кожним повторенням кожного
повторюваного тесту. Маючи TestInfo
та RepetitionInfo
ін'єктовані в метод, ми бачимо, що можливо отримати інформацію
щодо поточного виконуваного тесту. Виконання
RepeatedTestsDemo
з рівнем журналювання INFO
включає результати в наступний вивід.
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 3 of 10 for repeatedTest INFO: About to execute repetition 4 of 10 for repeatedTest INFO: About to execute repetition 5 of 10 for repeatedTest INFO: About to execute repetition 6 of 10 for repeatedTest INFO: About to execute repetition 7 of 10 for repeatedTest INFO: About to execute repetition 8 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 3 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 3 of 5 for repeatedTestInGerman INFO: About to execute repetition 4 of 5 for repeatedTestInGerman INFO: About to execute repetition 5 of 5 for repeatedTestInGerman
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.logging.Logger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;
class RepeatedTestsDemo {
private Logger logger = // ...
@BeforeEach
void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) {
int currentRepetition = repetitionInfo.getCurrentRepetition();
int totalRepetitions = repetitionInfo.getTotalRepetitions();
String methodName = testInfo.getTestMethod().get().getName();
logger.info(String.format("About to execute repetition %d of %d for %s", //
currentRepetition, totalRepetitions, methodName));
}
@RepeatedTest(10)
void repeatedTest() {
// ...
}
@RepeatedTest(5)
void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {
assertEquals(5, repetitionInfo.getTotalRepetitions());
}
@RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")
@DisplayName("Repeat!")
void customDisplayName(TestInfo testInfo) {
assertEquals(testInfo.getDisplayName(), "Repeat! 1/1");
}
@RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)
@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
з вімкненою темою юнікоду, виконання RepeatedTestsDemo
призведе до наступного виводу на консоль.
├─ 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 ✔
Параметризовані
тести роблять можливим виконувати тест декілька разів з різними
аргументами. Вони декларуються так само, як звичайні методи
@Test
,
але замість цього використовують анотацію @ParameterizedTest
.
На додаток, ви маєте декларувати щонайменьше одне джерело,
що буде провадити аргументи для кожного виклика, та потім споживати
ці аргументи в тестовому методі.
Наступний
тест демонструє параметризований тест, що використовує анотацію@ValueSource
,
щоб вказати масив String
як
джерело аргументів.
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
assertTrue(isPalindrome(candidate));
}
Коли
виконується параметризований тестовий метод вище, кожний виклик
буде рахуватись окремо. Наприклад, ConsoleLauncher
буде друкувати вивід подібний до наступного.
palindromes(String) ✔ ├─ [1] racecar ✔ ├─ [2] radar ✔ └─ [3] able was I ere I saw elba ✔
Параметризовані тести наразі знаходяться в стані експериментальної можливості. Консультуйтесь в таблиці Експирементальні API щодо деталей. |
Щоб
використовувати параметризовані тести, вам треба додати
залежність від артифакту junit-jupiter-params
.
Будь ласка звертайтесь до Метадані
залежностей щодо деталей.
Параметризований
тестовий метод типово споживає аргументи напряму зі
сконфігурованого джерела (дивіться Джерела
аргументів), слідуя кореляції один-до-одного між індексами
аргументів джерела та індексами параметрів метода (дивіться
приклади в @CsvSource).
Однак параметризований тест також може обрати агрегувати аргументи
з джерела в один об'єкт, що передається методу (дивіться Агрегація
аргументів). Додаткові аргументи можуть бути запроваджені
через ParameterResolver
(тобто,
отримати примірникіTestInfo
, TestReporter
,
etc.). Зокрема, метод параметризованого тесту має декларувати
формальні параметри відповідно до наступних правил.
Спочатку мають бути декларовані ноль або більше індексованих аргументів.
Далі декларуються ноль або більше агрегаторів.
Ноль
або більше аргументів, що постачаються ParameterResolver
мають бути декларовані останніми.
В
цьому контексті індексований аргумент є аргументом
для даного індексу в Arguments
,
запровадженому ArgumentsProvider
,
що передається як аргумент до параметризованого методу з тим
самим індексом в списку формальних параметрів метода. Агрегатор
є параметр типу ArgumentsAccessor
,
або любий параметр, анотований за допомогою@AggregateWith
.
Прямо
з коробки JUnit Jupiter провадить декілька анотацій джерел.
Кожне з наступних підрозділів провадить короткий огляд та
приклад кожного з них. Будь ласка посилайтесь до JavaDoc в
пакунку rg.junit.jupiter.params.provider
за
додатковою інформацією.
@ValueSource
є один з простіших можливих джерел. Він дозволяє вам
вказувати єдиний масив літеральних значень. Воно
дозволяє вам вказати один масив з літеральниз значень, та може
використовуватись тільки для провадження поодинокого аргументу
для кожного виклику параметризованого методу.
Підтримуються
наступні типи літеральних значень @ValueSource
.
short
byte
int
long
float
double
char
java.lang.String
java.lang.Class
Наприклад,
наступний метод @ParameterizedTest
буде викликаний три рази, зі значеннями 1
, 2
,
та 3
відповідно.
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
assertTrue(argument > 0 && argument < 4);
}
@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
дозволяє вам посилатись на один або більше методів фабрик
тестового класу або зовнішніх класів.
Метод
фабрики в тестовому класі має бути static
,
за винятком коли тестовий клас анотований за допомогою @TestInstance(Lifecycle.PER_CLASS)
;
з іншого боку, методи фабрик в зовнішніх класах мають завжди
бути static
.
На додаток такі методи фабрик ніколи не повинні мати
аргументів.
Кожний
метод фабрики має генерувати потік аргументів, та
кожний з аргументів в потоці буде проваджений як фізичний
аргумент для окремого виклику метода, анотованого @ParameterizedTest
.
Кажучи загалом, це транслює Stream
типу Arguments
(Stream<Arguments>
);
однак,
дійсний тип повернення може приймати декілька форм. В цьому
контексті "потік" є будь що, що JUnit може надійно перетворити
на Stream
,
таке як Stream
,
DoubleStream
,
LongStream
,
IntStream
,
Collection
, Iterator
, Iterable
,
любі масиви об'єктів, або любий масив примітивів. "Аргументи"
в потоці можуть бути постачатись як примірникиArguments
,
масив об'єктів (Object[]
),
або єдине значення, якщо метод параметризованого тесту приймає
єдиний аргумент.
Якщо
вам треба тільки єдиний параметр, ви можете повернути Stream
примірників типу параметра, як демонструє наступний
приклад.
@ParameterizedTest
@MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> stringProvider() {
return Stream.of("foo", "bar");
}
Якщо
ви явно не провадите ім'я метода фабрики через@MethodSource
,
JUnit Jupiter за домовленістю буде шукати метод фабрики,
що має те саме ім'я, що і поточний метод @ParameterizedTest
.
Це демонструється в наступному прикладі.
@ParameterizedTest
@MethodSource
void testWithSimpleMethodSourceHavingNoValue(String argument) {
assertNotNull(argument);
}
static Stream<String> testWithSimpleMethodSourceHavingNoValue() {
return Stream.of("foo", "bar");
}
Потоки
для примітивних типів (DoubleStream
, IntStream
,
and LongStream
)
також підтримуються, як демонструє наступний приклад.
@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(int argument) {
assertNotEquals(9, argument);
}
static IntStream range() {
return IntStream.range(0, 20).skip(10);
}
Якщо
параметризований тестовий метод декларує декілька параметрів,
вам треба повернути колекцію, потік або масив примірників Arguments
або масиви об'єктів, як показано нижче (дивіться
JavaDoc щодо @MethodSource
для подальших деталей по підтримуваним типам повернення).
Зауважте, що arguments(Object…
)
є статичним методом фабрикою, визначеною в
інтерфейсі Arguments
.
На додаток, Arguments.of(Object…
)
може бути використаний як альтернатива до
arguments(Object…
)
.
@ParameterizedTest
@MethodSource("stringIntAndListProvider")
void testWithMultiArgMethodSource(String str, int num, List<String> list) {
assertEquals(3, str.length());
assertTrue(num >=1 && num <=2);
assertEquals(2, list.size());
}
static Stream<Arguments> stringIntAndListProvider() {
return Stream.of(
arguments("foo", 1, Arrays.asList("a", "b")),
arguments("bar", 2, Arrays.asList("x", "y"))
);
}
Зовнішній,
static
метод фабрика може бути використаний через
провадження повністю кваліфікованого імені метода, як
демонструє наступний приклад.
package example;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class ExternalMethodSourceDemo {
@ParameterizedTest
@MethodSource("example.StringsProviders#tinyStrings")
void testWithExternalMethodSource(String tinyString) {
// test with tiny string
}
}
class StringsProviders {
static Stream<String> tinyStrings() {
return Stream.of(".", "oo", "OOO");
}
}
@CsvSource
дозвляє вам виражати список аргументів як
кома-розділені значення (i.e., String
literals).
@ParameterizedTest
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCsvSource(String first, int second) {
assertNotNull(first);
assertNotEquals(0, second);
}
@CsvSource
використовує поодинокі лапки '
як обмежуючий символ. Дивіться значення 'baz,
qux'
в прикладі вище, і таблиці нижче.
Пусті лапки ''
дають пустий рядок String
;
в той самий час повністю порожнє значення
інтерпретується як посилання null
.
Буде підійняте включення ArgumentConversionException
,
якщо цільовий тип посилання null
є примітивним типом.
Приклад вводу | Отриманий список аргументів |
---|---|
|
|
|
|
|
|
|
|
@CsvFileSource
дозволяє вам використовувати CSV файли в classpath.
Кожний рядок файла CSV дає один виклик параметризованого
тесту.
@ParameterizedTest
@CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1)
void testWithCsvFileSource(String first, int second) {
assertNotNull(first);
assertNotEquals(0, second);
}
Country, reference
Sweden, 1
Poland, 2
"United States of America", 3
На
відміну від синтаксису, що використовується в @CsvSource , @CsvFileSource
використовує подвійні лапки "
як обмежуючий символ. Дивіться значення"United
States of America" в прикладі вище.
Порожнє значення в лапках "" дає
порожній рядок String ;
пусте значення інтерпретується як посилання
null .
Підіймається виключенняArgumentConversionException ,
якщо цільовий тип посилання null є
примітивним типом. |
@ArgumentsSource
може використовуватись для вказання власного, повторо
використовуваногоArgumentsProvider
.
Зауважет, що реалізація ArgumentsProvider
має бути декларована або як клас вищого рівня, або як
вкладений static
клас.
@ParameterizedTest
@ArgumentsSource(MyArgumentsProvider.class)
void testWithArgumentsSource(String argument) {
assertNotNull(argument);
}
public class MyArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of("foo", "bar").map(Arguments::of);
}
}
JUnit
Jupiter підтримує Перетворення
поширення примітивів для аргументів, переданих до
@ParameterizedTest
.
Наприклад, параметризований текст, анотований з @ValueSource(ints
= { 1, 2, 3 })
, може бути декларований для
сприяння те тільки типу аргумента int
,
але також типів long
, float
,
або double
.
Для
підтримання випадків як @CsvSource
,
JUnit Jupiter провадить декілька вбудованих перетворень типів.
Процес перетворення залежить від декларованого типу кожного
параметру метода.
Наприклад,
якщо @ParameterizedTest
декларує параметр типу TimeUnit
,
та справжній тип, що постачає вказане джерело, є String
,
рядок буде автоматично перетворений в відповідну константу TimeUnit
enum.
@ParameterizedTest
@ValueSource(strings = "SECONDS")
void testWithImplicitArgumentConversion(TimeUnit argument) {
assertNotNull(argument.name());
}
String
примірникі наразі неявно перетворюються на наступні
цільові типи.
Цільовий тип | Приклад |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
На
додаток до неявного перетворення з рядків до цільових типів,
перелічених в таблиці вище, JUnit Jupiter також провадить
механізм відкочування для вітоматичного перетворення String
на певний цільовий тип, якщо цільовий тип декларує
підходящий метод фабрики, або конструктор
фабрику, як визначено нижче.
метод
фабрика: неприватний, static
метод, декларований в цільовому типі, що приймає
поодинокий аргумент String
,
та повертає примірник цільового типу. Ім'я метода може
бути довільним, та не має слідувати ніяким певним
домовленостям.
конструктор
фабрика: неприватний конструктор в цільовому
типі, що приймає поодинокий аргумент String
.
Зауважте, що цільовий тип має бути декларований або як
клас вищого рівня, або як вкладений static
клас.
Якщо буде знайдені декілька методів фабрик, вони будуть проігноровані. Якщо буде знайдений метод фабрики та конструктор файбрики, метод фабрики буде використаний замість конструктора. |
Наприклад,
в наступному методі @ParameterizedTest
,
аргумент Book
буде створений через виклик метод фабрику Book.fromTitle(String)
та передачу "42
Cats"
в якості назви книжки.
@ParameterizedTest
@ValueSource(strings = "42 Cats")
void testWithImplicitFallbackArgumentConversion(Book book) {
assertEquals("42 Cats", book.getTitle());
}
public class Book {
private final String title;
private Book(String title) {
this.title = title;
}
public static Book fromTitle(String title) {
return new Book(title);
}
public String getTitle() {
return this.title;
}
}
Замість
покладання на неявне перетворення, ви можете явно вказати ArgumentConverter
для використання для певного параметру, використовуючи
анотацію @ConvertWith
,
як в наступному прикладі. Зауважте, що реалізація ArgumentConverter
має бути декларована як клас вищого рівня, або як вкладений
static
клас.
@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithExplicitArgumentConversion(
@ConvertWith(ToStringArgumentConverter.class) String argument) {
assertNotNull(TimeUnit.valueOf(argument));
}
public class ToStringArgumentConverter extends SimpleArgumentConverter {
@Override
protected Object convert(Object source, Class<?> targetType) {
assertEquals(String.class, targetType, "Can only convert to String");
return String.valueOf(source);
}
}
Явні
перетворювачі аргументів призначені для реалізації авторами
тестів та розширень. Таким чином, junit-jupiter-params
провадить
тільки перетворювач одного явного аргумента, що також може
служити в якості референсної реалізації: JavaTimeArgumentConverter
.
Він використовується через компоновану анотацію JavaTimeConversionPattern
.
@ParameterizedTest
@ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(
@JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {
assertEquals(2017, argument.getYear());
}
По
замовчанню, кожний аргумент, проваджений до метода @ParameterizedTest
,
відповідає до одного параметра метода. Відповідно,
джерела аргументів, що очікують отримати велике число
аргументів, можуть призвести до великих сигнатур методів.
В
таких випадках може бути використаний ArgumentsAccessor
замість багатьох параметрів. Використання цього API
дає вам доступ до запроваджених аргументів через один аргумент,
переданий в тестовий метод. На додаток, підтримується
перетворення типів, як обговорюється в Неявних
перетвореннях.
@ParameterizedTest
@CsvSource({
"Jane, Doe, F, 1990-05-20",
"John, Doe, M, 1990-10-22"
})
void testWithArgumentsAccessor(ArgumentsAccessor arguments) {
Person person = new Person(arguments.getString(0),
arguments.getString(1),
arguments.get(2, Gender.class),
arguments.get(3, LocalDate.class));
if (person.getFirstName().equals("Jane")) {
assertEquals(Gender.F, person.getGender());
}
else {
assertEquals(Gender.M, person.getGender());
}
assertEquals("Doe", person.getLastName());
assertEquals(1990, person.getDateOfBirth().getYear());
}
Примірник
ArgumentsAccessor
автоматично ін'єктується в любий параметр типу ArgumentsAccessor
.
Окремо
від прямого доступу до аргументів тестового метода @ParameterizedTest
з використанням ArgumentsAccessor
,
JUnit Jupiter також підтримує використання власних, повторно
використовуваних агрегаторів.
Щоб
використати власний агрегатор, просто реалізуйте інтерфейс ArgumentsAggregator
,
зареєструйте його через анотацію @AggregateWith
на сумісному параметрів в методі @ParameterizedTest
.
Результат агрегації буде провадиться як аргумент для
відповідного параметра, коли викликається відповідний
параметризований тест. Зауважте, що реалізація ArgumentsAggregator
повинна бути декларована або клас вищого рівня, або як
вкладений static
клас.
@ParameterizedTest
@CsvSource({
"Jane, Doe, F, 1990-05-20",
"John, Doe, M, 1990-10-22"
})
void testWithArgumentsAggregator(@AggregateWith(PersonAggregator.class) Person person) {
// виконати твердження щодо особи
}
public class PersonAggregator implements ArgumentsAggregator {
@Override
public Person aggregateArguments(ArgumentsAccessor arguments, ParameterContext context) {
return new Person(arguments.getString(0),
arguments.getString(1),
arguments.get(2, Gender.class),
arguments.get(3, LocalDate.class));
}
}
Якщо
ви знайдете, що постійно декларуєте @AggregateWith(MyTypeAggregator.class)
для багатьох тестових методів в вашій кодовій базі, ви
можете створити власну композитну анотацію, як
@CsvToMyType
,
що є мета-анотована за допомогою @AggregateWith(MyTypeAggregator.class)
.
Наступний приклад демонструє це в дії з власною
анотацією @CsvToPerson
.
@ParameterizedTest
@CsvSource({
"Jane, Doe, F, 1990-05-20",
"John, Doe, M, 1990-10-22"
})
void testWithCustomAggregatorAnnotation(@CsvToPerson Person person) {
// виконати твердження щодо особи
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AggregateWith(PersonAggregator.class)
public @interface CsvToPerson {
}
По
замовчанню, дисплейне ім'я виклику параметризованого тесту
містить індекс виклику, та String
представлення
всіх аргументів для окремого виклику. Однак ви можете
налаштувати дисплейне ім'я викликів через атрибут name
анотації @ParameterizedTest
,
як в прикладі нижче.
@DisplayName("Display name of container")
@ParameterizedTest(name = "{index} ==> first=''{0}'', second={1}")
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCustomDisplayNames(String first, int second) {
}
При
виконанні метода вище з використанням ConsoleLauncher
ви побачите вивід, подібний до наступного.
Display name of container ✔ ├─ 1 ==> first='foo', second=1 ✔ ├─ 2 ==> first='bar', second=2 ✔ └─ 3 ==> first='baz, qux', second=3 ✔
Наступні підстановки підтримуютсья у власних дисплейних іменах.
Замінник | Опис |
---|---|
|
поточний номер виклику (від 1) |
|
повний, розділений комами, список аргументів |
|
окремий аргумент |
Кожний
виклик параметризованого тесту має той самий життєвий цикл, що і
звичайний метод @Test
.
Наприклад, методи @BeforeEach
будуть
викликатись перед кожним викликом. Подібно до Динамічних
тестів, виклики будуть з'являтись один за одним в
тестовому дереві IDE. Ви можете за бажанням міксувати звичайні
методи @Test
та
методи@ParameterizedTest
в
тому самому тестовому класі.
Ви
можете використовувати розширення ParameterResolver
з методом @ParameterizedTest
.
Однак, параметри методу, що розрішені джерелами вргументів,
мають іти першими в списку аргументів. Оскільи тестовий клас
може містити регулярні тести, так само, як і параметризовані
тести з різними списками параметрів, значення від джерел
аргументів не розрішуються для методів життєвого цикла (як
@BeforeEach
),
та конструкторів тестового класа.
@BeforeEach
void beforeEach(TestInfo testInfo) {
// ...
}
@ParameterizedTest
@ValueSource(strings = "foo")
void testWithRegularParameterResolver(String argument, TestReporter testReporter) {
testReporter.publishEntry("argument", argument);
}
@AfterEach
void afterEach(TestInfo testInfo) {
// ...
}
Метод
@TestTemplate
не є звичайним тестовим випадком, але скоріше шаблоном для
тестових випадків. Як такий, він розроблений бути
викликаний багато разів, в залежності від числа контекстів
викликів в реєстрованих провайдерах. Таким чином, він має бути
викликаний в поєднанні з зареєстрованим розширенням TestTemplateInvocationContextProvider
.
Кожний виклик метода тестового шаблона поводиться як виконання
звичайного метода @Test
з повною підтримкою для тих самих зворотніх викликів життєвого
цикла та розширень. Будь ласка посилайтесь до Провадження
контекстів викликів для тестових шаблонів щодо
прикладів виконання.
Стандартна
анотація @Test
в JUnit Jupiter, описана в Анотаціях
дуже подібна до анотації @Test
в
JUnit 4. Обоє описують методи, що реалізують тестові
випадки. Ці випадки статичні в тому сенсі, що вони повністю
специфіковані під час компіляції, та їх поведінка не може бути
змінена будь чим, що може трапитись під час виконання. Припущення
надають базову форму динамічної поведінки, але навмисне скоріше
обмежені в своїй виразності.
На
додаток до ціх стандартних тестів в JUnit Jupiter була введена
повністю нова модель програмування тестів. Цей новий тип є динамічні
тести, що генеруються під час виконання методом фабрикою,
анотованою за допомогою@TestFactory
.
На
відміну від методів @Test
,
метод @TestFactory
сам по собі не є тестовим випадком, але скоріше фабрикою
тестових випадків. Таким чином, динамічний тест є
продуктом фабрики. Технічно кажучи, метод @TestFactory
має повертати Stream
, Collection
, Iterable
, Iterator
,
або масив примірників DynamicNode
.
Можна створювати примірники субкласів 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.3.2, динамічні тести мають завжди бути створені методами фабрики; однак, це може бути доповнене можливістю реєстрації в наступному релізі.
Динамічні тести наразі є експериментальною можливістю. Consult the table in Experimental APIs for details. |
Наступний
клас DynamicTestsDemo
демонструє декілька прикладів фабрик тестів та динамічних
тестів.
Перший
метод повертає невірний тип повернення. Оскільки невалідний тип
повернення не може бути визначений під час компіляції, JUnitException
підіймається, коли це визначається під час виконання.
Наступні
п'ять методів є дуже простими прикладами, що демонструють
генерацію Collection
, Iterable
, Iterator
,
або Stream
примірників DynamicTest
.
Більшість з ціх прикладів насправді не показують динамічної
поведінки, але просто демонструють підтримувані типи повернення,
в принципі. Однак, dynamicTestsFromStream()
таdynamicTestsFromIntStream()
демонструють, як просто згенерувати динамічні тести для
заданого набору рядків, або диапазону вхідних чисел.
Наступний
метод справді динамічний по своїй натурі. generateRandomNumberOfTests()
реалізує Iterator
,
що генерує випадкові числа, генератор дисплейних імен, та
текстовий виконувач, та потім провадить всі три до DynamicTest.stream()
.
Хоча недетермінована поведінка generateRandomNumberOfTests()
,
звісно, конфліктує з повторюваністю тесту, та тому повинна
прийматись з застореженням, це служить демонстрації виразності
та потужності динамічних тестів.
Останній
метод генерує вкладену ієрархію динамічних тестів з
застосуванням DynamicContainer
.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;
class DynamicTestsDemo {
// Це пирзведе до JUnitException!
@TestFactory
List<String> dynamicTestsWithInvalidReturnType() {
return Arrays.asList("Hello");
}
@TestFactory
Collection<DynamicTest> dynamicTestsFromCollection() {
return Arrays.asList(
dynamicTest("1st dynamic test", () -> assertTrue(true)),
dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))
);
}
@TestFactory
Iterable<DynamicTest> dynamicTestsFromIterable() {
return Arrays.asList(
dynamicTest("3rd dynamic test", () -> assertTrue(true)),
dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2))
);
}
@TestFactory
Iterator<DynamicTest> dynamicTestsFromIterator() {
return Arrays.asList(
dynamicTest("5th dynamic test", () -> assertTrue(true)),
dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2))
).iterator();
}
@TestFactory
DynamicTest[] dynamicTestsFromArray() {
return new DynamicTest[] {
dynamicTest("7th dynamic test", () -> assertTrue(true)),
dynamicTest("8th dynamic test", () -> assertEquals(4, 2 * 2))
};
}
@TestFactory
Stream<DynamicTest> dynamicTestsFromStream() {
return Stream.of("A", "B", "C")
.map(str -> dynamicTest("test" + str, () -> { /* ... */ }));
}
@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<>() {
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, etc.
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()))
))
)));
}
}
По
замовчанню тести JUnit Jupiter виконуються послідовно в одному
потоці. Виконання тестів паралельно для підвищення швидкості також
можливе як додаткова можливість, з версії 5.3. Щоб дозволити
паралельне виконання, просто встановіть параметр конфігураціїjunit.jupiter.execution.parallel.enabled
в true
,
в junit-platform.properties
(дивіться
Параметри
конфігурації для інших опцій).
Коли вімкнено, рушій JUnit Jupiter буде виконувати тести на всіх рівнях тестування повністю паралельно, відповідно до провадженої конфігурації, при тому розглядаючи декларативні механізми синхронизації. Будь ласка, зауважте, що можливості Захоплення стандартного вводу-виводу потребує окремого дозволу.
Паралельне виконання тестів наразі є експериментальною можливістю. Ви запрошуєтесь спробувати на запровадити зворотній зв'язок до команди JUnit, так що вони зможуть покращити, та з часом просувати цю можливість. |
Властивості,
як бажаний паралелизм та максимальний розмір пула можуть бути
сконфігуровані з використанням ParallelExecutionConfigurationStrategy
.
JUnit Platform провадить дві реалізації прямо з коробки: dynamic
та fixed
.
Альтернативно, ви можете реалізувати custom
стратегію.
Щоб
обрать стратегію, просто встановіть параметр конфігурації
junit.jupiter.execution.parallel.config.strategy
в одну з наступних опцій:
dynamic
Обчислює
бажаний паралелизм базуючись на числі доступних
процесорів/ядер, помножених на параметр конфігурації junit.jupiter.execution.parallel.config.dynamic.factor
(по замовчанню 1
).
fixed
Використовує
обов'язковий параметр конфігурації junit.jupiter.execution.parallel.config.fixed.parallelism
в якості бажаного рівня паралелизму.
custom
Дозволяє
задати реалізацію ParallelExecutionConfigurationStrategy
через обов'язковий параметр конфігурації
junit.jupiter.execution.parallel.config.custom.class
для визначення бажаної конфігурації.
Якщо
стратегія конфігурації не встановлена, JUnit Jupiter
використовує конфігурацію dynamic
з фактором 1, тобто бажаний паралелизм буде дорівнювати
числу доступних процесорів/ядер.
В
пакунку org.junit.jupiter.api.parallel
JUnit Jupiter провадить два базованих на анотаціях
механізми для зміни режиму виконання, і дозволяє синхронізацію
при використанні розділених ресурсів в різних тестах.
Якщо
паралельне виконання дозволене, всі класи та методи виконуються
одночасно по замовчанню. Ви можете змінити режим виконання для
анотованого елементу та його субелементів (якщо є), через
виконання анотації @Execution
.
Доступні для режими:
SAME_THREAD
Примушує
виконання в тому ж потоці, що і батьківській тест.
Наприклад, коли використовується на тестовому методі,
тестовий метод буде виконуватись в тому ж потоці, що і любі
методи @BeforeAll
або @AfterAll
,
що містяться в тестовому класі.
CONCURRENT
Виконується одночасно, якщо ресурсне обмеження не примушує до виконання в тому ж потоці.
На
додаток анотація @ResourceLock
дозволяє декларувати, що тестовий клас або метод використовує
особливий розділений ресурс, що потребує синхронізований
доступ, щоб гарантувати надійне виконання тесту.
Якщо тести в наступному прикладі будуть виконуватись паралельно, вони будуть непевні, тобто іноді проходити, та іноді ні, завдяки стану гонок на записі, та потім зчитування тієї самої системної властивості.
@Execution(CONCURRENT)
class SharedResourcesDemo {
private Properties backup;
@BeforeEach
void backup() {
backup = new Properties();
backup.putAll(System.getProperties());
}
@AfterEach
void restore() {
System.setProperties(backup);
}
@Test
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ)
void customPropertyIsNotSetByDefault() {
assertNull(System.getProperty("my.prop"));
}
@Test
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
void canSetCustomPropertyToFoo() {
System.setProperty("my.prop", "foo");
assertEquals("foo", System.getProperty("my.prop"));
}
@Test
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
void canSetCustomPropertyToBar() {
System.setProperty("my.prop", "bar");
assertEquals("bar", System.getProperty("my.prop"));
}
}
Коли доступ до розділеного ресурса декларований з використанням анотації, рушій JUnit Jupiter використовує цю інформацію для переконання, що конфліктуючі тести не виконуються паралельно.
На
додаток до рядка, що унікально ідентифікує використаний ресурс,
ви можете задати режим доступу. Два тести, що потребуються
доступ READ
до ресурсу, можуть робити паралельно один з одним, але не з
іншим тестом, який потребує доступ READ_WRITE
.
IntelliJ
IDEA підтримує виконання тестів на JUnit Platform з версії
2016.2. Для деталей дивіться пост
в блозі IntelliJ IDEA. Однак зауважте, що рекомендовано
використовувати IDEA 2017.3 або новішу, оскількі ці нові версії
IDEA будуть завантажувати наступні JAR автоматично, базуючись на
версії API, використаної в проекті: junit-platform-launcher
, junit-jupiter-engine
,
та junit-vintage-engine
.
IntelliJ IDEA, випущена до IDEA 2017.3 включає специфічні версії JUnit 5. Таким чином, якщо ви бажаєте новішу версію JUnit Jupiter, виконання тестів в IDE може схибити через конфлікт версій. В таких випадках слідуйте інструкціям нижче, щоб використати новіші версії JUnit 5, ніж та, що іде з IntelliJ IDEA. |
Щоб
використати іншу версію JUnit 5 (5.3.2), вам може знадобитись
включити відповідну версію junit-platform-launcher
, junit-jupiter-engine
,
та junit-vintage-engine
JARs
в
classpath.
// Треба тільки в версіях IntelliJ IDEA, що включає старі тести
testRuntime("org.junit.platform:junit-platform-launcher:1.3.2")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.3.2")
testRuntime("org.junit.vintage:junit-vintage-engine:5.3.2")
<!--
Треба тільки в версіях IntelliJ IDEA, що включає старі тести
--> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>1.3.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.3.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <version>5.3.2</version> <scope>test</scope> </dependency>
Eclipse IDE пропонує підтримку JUnit Platform починаючи з релізу Eclipse Oxygen.1a (4.7.1a).
Для додаткової інформації по використанню JUnit 5 в Eclipse проконсультуйтесь з офіційним документом Eclipse support for JUnit 5 в розділі Eclipse Project Oxygen.1a (4.7.1a) - Нове та варте уваги.
На час написання немає прямої підтримки для виконання тестів на JUnit Platform в IDE, крім IntelliJ IDEA та Eclipse. Однак команда JUnit провадить два посередницькі рішення, з якими ви можете пійти далі, та спробувати JUnit 5 в вашій IDE вже сьогоді. Ви можете використати Заупск з консолі вручну, або виконати тести на JUnit 4 базованому Runner.
Починаючи
з версії
4.6, Gradle провадить природну
підтримку для виконання тестів на JUnit
Platform. Щоб вімкнути її, вам треба тільки вказати useJUnitPlatform()
в декларації завдання test
в build.gradle
:
test {
useJUnitPlatform()
}
Також підтримується фільтрація за тегами або рушіями:
test {
useJUnitPlatform {
includeTags 'fast', 'smoke & feature-a'
// excludeTags 'slow', 'ci'
includeEngines 'junit-jupiter'
// excludeEngines 'junit-vintage'
}
}
Будь ласка, посилайтесь до офіційної документації Gradle для вичерпного переліку опцій.
Gradle
Plugin для JUnit Platform більше не підтримується
Дуже
базовий |
Стандартне
завадання Gradle test
наразі не провадить окремий DSL для встановлення параметрів
конфігурації JUnit Platform, що впливають на пошук та
виконання тестів. Однак ви можете провадити параметри
конфігурації у вбудованому скрипті через системні
властивості (як показано нижче), або через файлjunit-platform.properties
.
test {
// ...
systemProperty 'junit.jupiter.conditions.deactivate', '*'
systemProperties = [
'junit.jupiter.extensions.autodetection.enabled': 'true',
'junit.jupiter.testinstance.lifecycle.default': 'per_class'
]
// ...
}
Щоб
виконувати любі тести взагалі, реалізація TestEngine
має знаходитись по classpath.
Щоб
сконфігурувати підтримку тестів на основі JUnit Jupiter,
сконфігуруйте залежність testCompile
на JUnit Jupiter API , та залежність testRuntime
на реалізацію JUnit Jupiter TestEngine
,
подібно до наведеного нижче.
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-api:5.3.2")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.3.2")
}
JUnit
Platform може виконувати тести JUnit 4, доки ви конфігуруєте
залежність testCompile
на JUnit 4, та залежністьtestRuntime
на реалізацію JUnit Vintage TestEngine
,
подібно до наступного.
dependencies {
testCompile("junit:junit:4.12")
testRuntime("org.junit.vintage:junit-vintage-engine:5.3.2")
}
JUnit
використовує
Java Logging API в пакунку java.util.logging
(a.k.a. JUL),
що видавати попередження та інформацію налаштування. Будь
ласка посилайтесь на офіційну документацію по LogManager
щодо опцій конфігурації.
Альтернативно,
можливо переслати повідомлення журналу на інший фреймворк,
такий як Log4j
або Logback.
Щоб використовувати журнальний фреймворк, що провадить власну
реалізацію LogManager
,
встановіть системну властивість java.util.logging.manager
в повністю клаліфіковане ім'я класу реалізації
LogManager
,
що треба використовувати. Приклад нижче демонструє,
як сконфігурувати Log4j 2.x (дивіться Log4j
JDK Logging Adapter щодо деталей).
test {
systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
}
Інші
журналні фреймворки провадять різні можливості по
перепризначенню журнальних повідомлень з використанням java.util.logging
.
Наприклад, для Logback
ви можете використати JUL
to SLF4J Bridge через додавання додаткової
залежності в classpath.
Власний
junit-platform-surefire-provider ,
що був оригінально розроблений командою JUnit, пішов у
відставку, та запланований для видалення в JUnit Platform
1.4. Будь ласка, використовуйте замість нього природну
підтримку Maven Surefire. |
Починаючи
з версії
2.22.0, Maven Surefire провадить природну
підтримку для виконання тестів на JUnit
Platform. Файл pom.xml
в проекті junit5-jupiter-starter-maven
демонструє, як використати її, та може служити як стартова
точка для конфігурації вашої побудови Maven.
Щоб
Maven Surefire взагалі міг виконувати будь-які тести,
щонайменьше одна реалізація TestEngine
має бути додана до тестового classpath.
Щоб
сконфігурувати підтримку для тестів на основі JUnit Jupiter,
сконфігуруйте залежності області test
на JUnit Jupiter API та реалізацію JUnit Jupiter TestEngine
,
подібно до наступного.
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
...
<dependencies>
...
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
...
</dependencies>
...
Maven
Surefire може виконувати тести на основі JUnit 4 поряд з
тестами Jupiter, доки ви сконфігуруєте залежності test
на JUnit 4 та на реалізацію JUnit Vintage TestEngine
,
подібно до наступного.
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
...
<dependencies>
...
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
...
</dependencies>
...
Плагін Maven Surefire буде сканувати в пошуках тестових класів, чиє повне ім'я співпадає з наступними шаблонами.
**/Test*.java
**/*Test.java
**/*Tests.java
**/*TestCase.java
Більше того, він буде виключати всі вкладені класи (включаючи класи статичних членів) по замовчанню.
Однак
зауважте, що ви можете перекрити поведінку по замовчанню, явно
сконфігурувавши правила include
та exclude
в вашому файлі pom.xml
.
Наприклад, щоб утримати Maven Surefire від виключення класів
статичних членів, ви можете переписати його правила виключення
таким чином.
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<excludes>
<exclude/>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
...
Будь ласка, дивіться документацію Включення та виключення тестів для Maven Surefire щодо деталей.
Ви можете фільтрувати тести за тегами або за теговими виразами, використовуючи наступні властивості конфігурації.
для
включення тегів або тегових виразів,
використовуйте groups
.
щоб
виключити теги або тегові вирази, використовуйте
excludedGroups
.
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<groups>acceptance | !feature-a</groups>
<excludedGroups>integration, regression</excludedGroups>
</configuration>
</plugin>
</plugins>
</build>
...
Ви
можете встановити для JUnit Platform параметри
конфігурації, що впливатимуть на пошук та
виконання тестів, декларуючи властивістьconfigurationParameter
властивість, та провадячи пари ключ-значення
використовуючи синтаксис файла Java Properties
(як показано нижче), або через файл junit-platform.properties
.
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<properties>
<configurationParameters>
junit.jupiter.conditions.deactivate = *
junit.jupiter.extensions.autodetection.enabled = true
junit.jupiter.testinstance.lifecycle.default = per_class
</configurationParameters>
</properties>
</configuration>
</plugin>
</plugins>
</build>
...
Починаючи
з версії 1.10.3
Ant
вводить нове завдання junitlauncher
,
щоб провадити природну підтримку для запуску тестів на JUnit
Platform. Задачаjunitlauncher
суцільно відповідає за запуск JUnit Platform, та
передачу їй обраного набору тестів. Потім JUnit Platform делегує
зареєстровані тестові рушії, щоб винайти та виконати тести.
Завдання
junitlauncher
намагається
дотримуватись як це можливо до природних конструкцій Ant,
як колекції
ресурсів, щоб дозволити користувачам обирати тести,
які пони бажають виконати в тестових рушіях. Це робить
завдання сумісним та надає природного відчуття, в порівнянні з
багатьма іншими завданнями Ant.
Версія
завдання junitlauncher ,
яка надходить з Ant 1.10.3, провадить базову,
мінімальну підтримку для зупуску JUnit Platform. Додаткові
розширення (включаючи підтримку для відгалуження
тестів в окремі JVM) будуть доступні в наступних
релізах Ant. |
Файл
build.xml
в проекті junit5-jupiter-starter-ant
демонструє, як використовувати це завдання, та може служити
початковою точкою.
Наступні
приклади доменструють, як зконфігурувати завдання junitlauncher
для обрання одного тесту (org.myapp.test.MyFirstJUnit5Test
).
<path id="test.classpath">
<!-- Місце, де ви маєте свої скомпільовані файли -->
<pathelement location="${build.classes.dir}" />
</path>
<!-- ... -->
<junitlauncher>
<classpath refid="test.classpath" />
<test name="org.myapp.test.MyFirstJUnit5Test" />
</junitlauncher>
Елемент
test
дозволяє вам вказати окремий тестовий клас, якій ви бажаєте
вибрати або виконати. Елемент classpath
дозволяє
вам задати classpath, що використовується при
запуску JUnit Platform. Цей classpath також буде
використовуватись для пошуку тестових класів, що будуть
частиною виконання.
Наступний
приклад демонструє, як сконфігурувати завдання junitlauncher
,
щоб вибрати тестові класи з декількох розташувань.
<path id="test.classpath">
<!-- Місце, де ви маєте свої скомпільовані файли -->
<pathelement location="${build.classes.dir}" />
</path>
....
<junitlauncher>
<classpath refid="test.classpath" />
<testclasses outputdir="${output.dir}">
<fileset dir="${build.classes.dir}">
<include name="org/example/**/demo/**/" />
</fileset>
<fileset dir="${some.other.dir}">
<include name="org/myapp/**/" />
</fileset>
</testclasses>
</junitlauncher>
В
прикаладі вище елемент testclasses
дозволяє вам обрати декілька тестових, що знаходяться в
різних місцях.
Для
додаткових деталей щодо використання та опцій конфігурації,
будь ласка посилайтесь до офіційної документації Ant в
частині завданняjunitlauncher
task.
ConsoleLauncher
є застосуванням Java командного рядка, щоб запускати JUnit
Platform з консолі. Наприклад, вона може бути використана для
виконання тестів JUnit Vintage та JUnit Jupiter, та друкування
результатів виконання в консолі.
Виконавчий
файл junit-platform-console-standalone-1.3.2.jar
з усіма залежностями включений в публікацію на центральному
репозитарії Maven в директорії junit-platform-console-standalone.
Ви можете запустити
самостійний ConsoleLauncher
,
як показано нижче.
java
-jar junit-platform-console-standalone-1.3.2.jar <Options>
Ось приклад отриманого результату:
├─ 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 tests successful ] [ 0 tests failed ]
Код
завершення
ConsoleLauncher
виходить з кодом статуса 1 ,
якщо любі контейнери або тести схиблять. Якщо тести
не були знайдені, та надана опція командного рядка --fail-if-no-tests ,
ConsoleLauncher
виходить з кодом стану 2 .
Інакше код завершення є 0 . |
Використання: ConsoleLauncher [-h] [--disable-ansi-colors] [--fail-if-no-tests] [--scan-modules] [--scan-classpath[=PATH[;|:PATH...]]]... [--details=MODE] [--details-theme=THEME] [--reports-dir=DIR] [--config=KEY=VALUE]... [--exclude-package=PKG]... [--include-package=PKG]... [-c=CLASS]... [-cp=PATH[;|:PATH...]]... [-d=DIR]... [-e=ID]... [-E=ID]... [-f=FILE]... [-m=NAME]... [-n=PATTERN]... [-N=PATTERN]... [-o=NAME]... [-p=PKG]... [-r=RESOURCE]... [-t=TAG]... [-T=TAG]... [-u=URI]... Запускає JUnit Platform з консолі. -h, --help Показує цю підказку. --disable-ansi-colors Відключає ANSI кольорі на виході (не підтримується всіма терміналами). --details=MODE Обирає режим деталей виводу для виконання тестів. Використовуйте одне з наступного: none, summary, flat, tree, verbose. Якщо 'none', показуються тільки підсумки та сбої тестів. Замовчання: tree. --details-theme=THEME Обираєn output details tree theme for when tests are executed. Use one of: ascii, unicode. Default: unicode. -cp, --classpath, --class-path=PATH[;|:PATH...] Provide additional classpath entries -- for example, for adding engines and their dependencies. Може повторюватись. --fail-if-no-tests Fail and return exit status code 2 if no tests are found. --reports-dir=DIR Enable report output into a specified local directory (will be created if it does not exist). --scan-modules EXPERIMENTAL: Scan all resolved modules для пошуку тестів. -o, --select-module=NAME EXPERIMENTAL: Select single module для пошуку тестів. Може повторюватись. --scan-classpath, --scan-class-path[=PATH[;|:PATH...]] Scan all directories on the classpath or explicit classpath roots. Without arguments, only directories on the system classpath as well as additional classpath entries supplied via -cp (directories and JAR files) are scanned. Explicit classpath roots that are not on the classpath will be silently ignored. Може повторюватись. -u, --select-uri=URI Обирає URI для пошуку тестів. Може повторюватись. -f, --select-file=FILE Обирає file для пошуку тестів. Може повторюватись. -d, --select-directory=DIR Обирає directory для пошуку тестів. Може повторюватись. -p, --select-package=PKG Обирає package для пошуку тестів. Може повторюватись. -c, --select-class=CLASS Обирає class для пошуку тестів. Може повторюватись. -m, --select-method=NAME Обирає method для пошуку тестів. Може повторюватись. -r, --select-resource=RESOURCE Обирає classpath resource для пошуку тестів. Може повторюватись. -n, --include-classname=PATTERN Provide a regular expression to include only classes whose fully qualified names match. To avoid loading classes unnecessarily, the default pattern only includes class names that begin with "Test" or end with "Test" or "Tests". When this option is repeated, all patterns will be combined using OR semantics. Default: [^(Test.*|.+[.$]Test.*|.*Tests?)$] -N, --exclude-classname=PATTERN Provide a regular expression to exclude those classes whose fully qualified names match. When this option is repeated, all patterns will be combined using OR semantics. --include-package=PKG Провадить пакунок to be included in the test run. Може повторюватись. --exclude-package=PKG Провадить пакунок to be excluded from the test run. Може повторюватись. -t, --include-tag=TAG Провадить тест або теговий вираз to include only tests whose tags match. When this option is repeated, all patterns will be combined using OR semantics. -T, --exclude-tag=TAG Провадить тест або теговий вираз to exclude those tests whose tags match. When this option is repeated, all patterns will be combined using OR semantics. -e, --include-engine=ID Провадить ID тестового рушія to be included in the test run.
Може повторюватись. -E, --exclude-engine=ID Провадить ID тестового рушія to be excluded from the test run. Може повторюватись. --config=KEY=VALUE Set a configuration parameter для пошуку тестів and execution. Може повторюватись.
На деяких платформах ви можете натрапити на системні обмеження до довжини командного рядка, коли створюєте командний рядок з багатьма опціями або довгими аргументами.
Починаючи
з версії 1.3, ConsoleLauncher
підтримує файли аргументів, також відомі як @-файли.
Файли аргументів є файлами, що самі по собі містять аргументи,
які будуть передані в команду. Коли службова команда розбору
командного рядка picocli натрапляє
на аргумент, що починається з символа @
,
вона розширює вміст цього файла в список аргументів.
Аргументи
а файлі можуть бути розділені проміжками або новими рядками.
Якщо аргумент містить проміжок, цілий аргумент має бути
огорнений в подвійні або поодинокі лапки — наприклад, "-f=My
Files/Stuff.java"
.
Якщо
файл аргументів не існує, або не може бути прочитаний, аргумент
буде розглядатись буквально, та не буде видалений. Це буде з
вірогідністю призводити до повідомлення про помилку "unmatched
argument". Ви можете вірішувати такі помилки через виконання
команд з системною властивістю picocli.trace
,
встановленою в DEBUG
.
В одниму рядку можна задати декілька @-файлів. Вказаний шлях може бути відносним до поточної директорії, або абсолютним.
Ви
можете передати реальний параметр, що починається з символа
@
,
додаючи ще один символ @
.
Наприклад, @@somearg
перетвориться на @somearg
,
та не буде приводом для розширення.
Раннер
JUnitPlatform
базується на JUnit 4 Runner
,
що дозволяє вам виконувати любі тести, чия програмна модель є
підтримованою на JUnit Platform в оточенні JUnit 4
— наприклад, тестовий клас JUnit Jupiter.
Анотація
класа за допомогою @RunWith(JUnitPlatform.class)
дозволяє
виконувати його в IDE та системах побудови, що
підтримують JUnit 4, та все ще не підтримують JUnit Platform.
Оскільки
JUnit Platform має можливості, якіх не має JUnit 4, раннер в
змозі підтримувати тільки субмножину функціональності
JUnit Platform, особливо в частині доповідей (дивіться
Дисплейні
імена vs. Технічні імена). Але на даний час раннер
JUnitPlatform
є простим шляхом розпочати роботу. |
Вам знадобляться наступні артифакти та їх залежності у вашому classpath. Дивіться метадані залежностей для деталей щодо групових ID, артифактних ID та версій.
junit-platform-runner
в полі test:
розміщення раннера JUnitPlatform
junit-4.12.jar
в полі test:
щоб виконувати тести з використанням JUnit 4
junit-jupiter-api
в полі test:
API для написання тестів з використаннямJUnit Jupiter,
включаючи @Test
,
etc.
junit-jupiter-engine
в полі test
runtime: реалізація TestEngine
API
для
JUnit Jupiter
junit-platform-suite-api
в полі test
junit-platform-launcher
в полі test
junit-platform-engine
в полі test
junit-platform-commons
в полі test
opentest4j
в полі test
Щоб
визначити власне дисплейне ім'я для класу, що
виконується через @RunWith(JUnitPlatform.class)
,
просто анотуйте клас за допомогою @SuiteDisplayName
,
та зщапровадьте власне значення.
По
замовчанн, дисплейне ім'я буде використовуватись для
тестових артифактів; однаколи раннер JUnitPlatform
використовується для виконання тестів з інструментами
побудови, такими, як Gradle або Maven,
згенерована доповідь часто потребує включення технічних
імен тестових артифактів — наприклад, повністю
кваліфіковані імена класів, замість коротших дисплейних імен
тестового класу або власного дисплейного імені, що містить
спеціальні символи. Щоб дозволити технічні імена для цілей
презентації, просто декларуйте анотацію @UseTechnicalNames
разом з @RunWith(JUnitPlatform.class)
.
Зауважте,
що присутність @UseTechnicalNames
перекриває любі власні дисплейні імена, зконфігуровані через
@SuiteDisplayName
.
Один
спосіб використати раннер JUnitPlatform
є анотація тестового класу за допомогою @RunWith(JUnitPlatform.class)
напряму. Будь ласка, зауважте, що тестові методи в
наступному прикладі анотовані за допомогою org.junit.jupiter.api.Test
(JUnit
Jupiter), а не org.junit.Test
(JUnit
Vintage). Більше того, в цьому випадку тестові класи мають бути
public
;
інакше деякі IDE і інструменти побудови можуть не розпізнати це
як тестовий клас JUnit 4.
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
@RunWith(JUnitPlatform.class)
public class JUnit4ClassDemo {
@Test
void succeedingTest() {
/* no-op */
}
@Test
void failingTest() {
fail("Failing for failing's sake.");
}
}
Якщо ви маєте декілька тестових класів, ви можете створити тестову сюїту, як можна побачити в наступному прикладі.
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.SuiteDisplayName;
import org.junit.runner.RunWith;
@RunWith(JUnitPlatform.class)
@SuiteDisplayName("JUnit 4 Suite Demo")
@SelectPackages("example")
public class JUnit4SuiteDemo {
}
JUnit4SuiteDemo
буде знаходити та виконувати всі тести в пакунку example
,
та його субпакунках. По замовчанню, це буде включати
тільки тестові класси, чиє ім'я починається на Test
,
або завершуватись на Test
або
Tests
.
Додаткові
опції конфігурації
Існує більше опцій конфігурації для пошуку та фільтрації
тестів, ніж тільки @SelectPackages .
Звертайтесь до Javadoc
для подальших деталей. |
На
додаток до інструкцій платформі, які тестові класи та тестові
рушії включати, які пакунки сканувати, тощо, іноді потрібно
запровадити власні додаткові параметри конфігурації, що специфічні
до окремого зареєстрованого плагіну тестового рушія. Наприклад,
JUnit Jupiter TestEngine
підтримує параметри
конфігурації для наступних випадків.
Параметри конфігурації це текстові пару ключ-значення, що можуть постачатись до тестового рушія, що виконується на платформі JUnit Platform через один з наступних механізмів.
Методи
configurationParameter()
та configurationParameters()
в LauncherDiscoveryRequestBuilder
,
що використовується для побудови запиту, що передається в Launcher
API.
Коли виконуються тести через один з інструментів, наданих
JUnit Platform, ви можете вказати параметри конфігурації таким
чином:
Консольний
рядок: використовуйте опцію командного рядка --config
.
Gradle:
використовуйте DSL systemProperty
або systemProperties
.
Maven
Surefire provider: використовуйте властивість
configurationParameters
.
Системні властивості JVM.
Файл
конфігурації JUnit Platform: файл на ім'я junit-platform.properties
в корені шляху класів, що слідує синтаксичним правилам для
файлу Java Properties
.
Параметри
конфігурації переглядаються саме в тому порядку, який
визначений вище. Відповідно, параметри конфігурації, надані
напряму до Launcher
мають перевагу над тими, що надані через системні
властивості та файл конфігурації. Подібно,
параметри конфігурації, надані через системні властивості
мають перевагу над тими, що надаються через файл
конфігурації. |
Тегові
вирази є логічними виразами з операторами !
, &
та |
.
На додаток можна використовувати (
та )
для зміни порядку виконання.
Оператор | Значення | Асоціативність |
---|---|---|
|
not |
права |
|
and |
ліва |
|
or |
ліва |
Якщо ви націлюєте свої тести між різними вимірами, тегові вирази допомагають вам обрати, які тести виконувати. Для тегування з типом тестів ( micro, integration, end-to-end) та можливостями (foo, bar, baz) можуть бути корисні такі вирази.
Теговий вираз | Вибір |
---|---|
foo |
всі тести для foo |
bar | baz |
всі тести для bar плюс всі тести для baz |
bar & baz |
всі тести для перетину bar та baz |
foo & !end-to-end |
всі тести для foo, але не тести end-to-end |
(micro | integration) & (foo | baz) |
всі тести micro або integration для foo або baz |
Починаючи
з версії 1.3, JUnit Platform провадить додаткову підтримку
для захоплення стандартного виводу на System.out
та System.err
.
Щоб вімкнути це, просто встановіть параметри
конфігураціїjunit.platform.output.capture.stdout
та/або junit.platform.output.capture.stderr
в
true
.
На
додаток ви можете конфігурувати максимальне число байтів
буферу, що треба використовувати перед кожним тестом або
використанням контейнера, використовуючи junit.platform.output.capture.maxBuffer
.
Якщо
вімкнено, JUnit Platform захоплює відновідний вивід та
публікує його як елемент доповіді, використовуючи ключі stdout
або stderr
для всіх зареєнстрованих примірників TestExecutionListener
безпосередньо
перед сповіщенням про завершення тесту.
Будь
ласка, зауважте, що захоплений вивід буде містити тільки видачу
потоку, що був використаний для запуску контейнера або
тесту. Любий вивід з інших потоків буде втрачений, бо,
зокрема коли тести
виконуються паралельно, буде неможливим приписати
вивід окремому тесту або контейнеру.
Захоплення виводу наразі є експериментальною можливістю. Ви запрошені спробувати та надати зворотній зв'язок до команди JUnit, так щоб вони змогли покращити, та з часом просувати цю можливість. |
На
відміну від конкуруючих точок розширення Runner
, @Rule
та
@ClassRule
в JUnit 4, модель розширення JUnit Jupiter
складається з єдиної, узгодженої концепції: Extension
API.
Однак зауважте, що сам по собі Extension
є лише маркерним інтерфейсом.
Розширення
можуть бути зареєстровані декларативно,
через@ExtendWith
,
програмно
через @RegisterExtension
,
або автоматично
через мехаеізм Java ServiceLoader
.
Розробники
можуть реєструвати одне або більше розширень декларативно,
через анотацію тестового інтерфейсу, тестового класу,
тестового методу, або власної композитної
анотації за допомогою @ExtendWith(…
),
та надаючи класові посилання для реєстрованих
розширень.
Наприклад,
щоб зареєструвати власне RandomParametersExtension
для окремого тестового метода, вам слідує анотувати тестовий
метод таким чином.
@ExtendWith(RandomParametersExtension.class)
@Test
void test(@Random int i) {
// ...
}
Щоб
зареєструвати RandomParametersExtension
для всіх тестів в окремому класі та його субкласах, вам треба
анотувати тестовий клас таким чином.
@ExtendWith(RandomParametersExtension.class)
class MyTests {
// ...
}
Декілька розширень можуть бути зареєстровані разом:
@ExtendWith({ FooExtension.class, BarExtension.class })
class MyFirstTests {
// ...
}
Як альтернатива, декілька розширень можуть бути зареєстровані окремо, ось так:
@ExtendWith(FooExtension.class)
@ExtendWith(BarExtension.class)
class MySecondTests {
// ...
}
Порядок
реєстрації розширень
Розширення, що реєструються декларативно через @ExtendWith
будуть виконуватись в порядку, в якому вони декларовані
в джерельному коді. Наприклад, виконання тестів в обох,
MyFirstTests
та MySecondTests
будуть розширені FooExtension
та BarExtension ,
точно в такому порядку. |
Розробники
можуть реєструвати розширення програмно, через
анотацію тестового класу за допомогою @RegisterExtension
.
Коли
розширення реєструється декларативно через @ExtendWith
,
типово воно може бути сконфігуроване тільки через анотації. Для
контрасту, коли розширення реєструється через @RegisterExtension
,
воно може бути сконфігуроване програмно
— наприклад, щоб передати аргументи до конструктора розширення,
статичного метода фабрики або API білдера.
@RegisterExtension поля
не мають бути ні private
ні null (під
час обчислення) , але можуть бути абоstatic ,
або не-статичними. |
Якщо
поле @RegisterExtension
є static
,
розширення буде зареєстроване після розширень, що
зареєстровані на рівні класу за допомогою @ExtendWith
.
Такі статичні розширення не обмежуються в тому, які
API розширення вони можуть реалізувати. Розширення,
зареєстровані через статичні поля, таким чином можуть
реалізувати API розширення рівня класу та рівня примірника,
такі, як BeforeAllCallback
, AfterAllCallback
та TestInstancePostProcessor
,
так само, як розширення рівня методу, як BeforeEachCallback
,
etc.
В
наступному прикладі поле server
в тестовому класі ініціалізоване програмно, через
використання паттерну білдера, що підтримується WebServerExtension
.
Сконфігуроване WebServerExtension
буде
автоматично зареєстроване як розширення на рівні класу
— наприклад, щоб запустити сервер перед всіма тестами в
класі, та потім зупинити сервер після закінчення всіх тестів в
класі. На додаток, статичні методи життєвого циклу, анотовані
@BeforeAll
або @AfterAl,
так само, як методів
@BeforeEach
, @AfterEach
,
та @Test
може
отримувати доступ до примірника розширення через поле
server
,
якщо це необхідно.
class WebServerDemo {
@RegisterExtension
static WebServerExtension server = WebServerExtension.builder()
.enableSecurity(false)
.build();
@Test
void getProductList() {
WebClient webClient = new WebClient();
String serverUrl = server.getServerUrl();
// Використовує WebClient для з'єднання з сервером через serverUrl
assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
}
}
Мова
програмування Kotlin не має концепції static
полів. Однак компілятор може бути поінструктований
для генерації статичних полів, використовуючи анотації.
Оскільки, як вказано вище, поля @RegisterExtension
не мають бути ні private
ні null
,
ви не можете використовувати
анотацію @JvmStatic
в Kotlin, оскільки це генерує поля private
.
Скоріше треба використовувати анотацію @JvmField
.
Наступний
приклад є версією WebServerDemo
з попереднього розділу, що портований на Kotlin.
class KotlinWebServerDemo { companion object { @JvmField @RegisterExtension val server = WebServerExtension.builder() .enableSecurity(false) .build() } @Test fun getProductList() {
// Використовує WebClient для з'єднання з сервером через serverUrl
val webClient = WebClient() val serverUrl = server.serverUrl assertEquals(200, webClient.get("$serverUrl/products").responseStatus) } }
Якщо
поле @RegisterExtension
нестатичне (тобто поле примірника), розширення буде
зареєстроване після створення примірника тестового класа, та
після того, як всі зареєстровані TestInstancePostProcessor
отримають
шанс після-обробити тестовий примірник (потенційно
зробивши ін'єкцію розширення для використання в анотоване
поле). Таким чином, якщо таке розширення примірника реалізує
API розширення рівня класу та рівня примірника, такі, як BeforeAllCallback
, AfterAllCallback
,
або TestInstancePostProcessor
,
ці API будуть поважатись. По замовчанню розширення
примірника реєструється після розширень, що
реєструються на рівні метода через @ExtendWith
;
однак, якщо тестовий клас сконфігурований з семантикою @TestInstance(Lifecycle.PER_CLASS)
,
розширення примірника буде зареєстрований перед розширеннями,
зареєстрованими на рівні методу через @ExtendWith
.
В
наступному прикладі поле docs
тестового класу ініціалізоване програмно, через виклик
власного метода lookUpDocsDir()
,
та надання результату до статичного метода фабрики
forPath()
в DocumentationExtension
.
Зконфігуроване DocumentationExtension
буде автоматично зареєстроване як розширення на рівні метода.
На додаток методи @BeforeEach
, @AfterEach
та
@Test
можуть отримувати доступ до примірника розширення через полеdocs
,
якщо є потреба.
class DocumentationDemo {
static Path lookUpDocsDir() {
// повертає шлях до папки з документами
}
@RegisterExtension
DocumentationExtension docs = DocumentationExtension.forPath(lookUpDocsDir());
@Test
void generateDocumentation() {
// використовує this.docs ...
}
}
На
додаток до декларативної
реєстрації розширення та програмної
реалізації розширення за допомогою анотацій, JUnit
Jupiter також підтримує глобальну реєстрацію розширень через
механізм Java java.util.ServiceLoader
,
дозволяючи стороннім розширенням бути само-детектованими та
автоматично зареєстрованими на основі того, що доступне в
classpath.
Більш
точно, власні розширення можуть бути зареєстровані через надання
його повністю квалифікованого шляху класа в файлі з ім'ям
org.junit.jupiter.api.extension.Extension
в папці /META-INF/services
в оточуючому JAR файлі.
Авто-детекція
є просунутою можливістю, і, таким чином, не вімкнена по
замовчанню. Щоб вімкнути її, просто встановіть параметр
конфігурації junit.jupiter.extensions.autodetection.enabled
в true
.
Це може виглядати як системна властивівсть JVM, як параметр
конфігурації в LauncherDiscoveryRequest
,
що передається в Launcher
,
або через файл конфігурації JUnit Platform (дивіться параметри
конфігурації щодо деталей).
Наприклад, щоб вімкнути авто-детекцію розширень ви можете запустити вашу JVM з наступною системною властивістю.
-Djunit.jupiter.extensions.autodetection.enabled=true
Коли
авто-детекція вімкнена, розширення , знайдені через механізмServiceLoader
будуть додані до реєстру розширень після глобальних
розширень JUnit Jupiter (підтримка для TestInfo
, TestReporter
,
etc.).
Зареєстровані розширення наслідуються в ієрархіях тестових класів з семантикою згори-донизу. Подібно до цього, розширення, зареєстровані на рівні класу, наслідуються на рівні методів. Більше того, специфчна реалізація може бути зареєстрована тільки один раз для даного контексту розширення, а також батьківськіх контекстів. Відповідно, люба спроба зареєструвати дублікат реалізації розширення буде проігнорована.
ExecutionCondition
визначає Extension
API
для програмного, умовного виконання тестів.
ExecutionCondition
обчислюється для кожного контейнера
(тестового класу) для визначення, чи всі тести в контейнері
повинні бути обчислені, базуючись на наданомуExtensionContext
.
Подібно до цього, ExecutionCondition
обчислюється для кожного тесту, щоб визначити, чи
даний тестовий метод повинен бути виконаний, базуючись на
наданому ExtensionContext
.
Коли
зареєстровано декілька розширень ExecutionCondition
,
контейнер або тест буде відключений так скоро, як одна з умов
повертає відключено.
Таким чином, немає гарантії, що умова обчислюється, оскільки інше
розширення вже може спричинити відключення теста або контейнера.
Іншими словами, обчислення робить як скорочене логічне OR.
Дивіться
джерельний код DisabledCondition
та @Disabled
для конкретних прикладів.
Іноді
може бути корисним виконати тестову сюїту без активації
певних умов. Наприклад, ви можете побажати виконати
тести, навіть якщо вони анотовані за допомогою @Disabled
,
щоб переконатись, що вони все ще зламаті. Щоб
зробити це, просто запровадьте шаблон до параметра
конфігурації
junit.jupiter.conditions.deactivate
,
щоб вказати, які умови мають бути відключені (не обчислюватись)
для поточного запуску тесту. Шаблон може бути доставлений як
системна властивість JVM, як параметр конфігурації
в LauncherDiscoveryRequest
,
що передається в Launcher
,
або через файл конфігурації JUnit Platform (дивіться параметри
конфігурації щодо деталей).
Наприклад,
щоб деактивувати умову 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
:
деактивує умову, чиє FQCN є саме org.example.MyCondition
.
TestInstanceFactory
визначає API для Extensions
,
що бажають створювати примірники тестів.
Загальні випадки використання включають приєднання примірника тесту з фреймворку ін'єкції залежностей, або виклик метода фабрики, щоб створити примірник тестового класу.
Якщо
немає зареєстрованого TestInstanceFactory
,
фреймворк буде просто викликати єдиний конструктор для
тестового класу для створення його примірника, потенційно
розрішуючи аргументи конструктора через зареєстровані розширення ParameterResolver
.
Розширення,
що реалізують TestInstanceFactory
,
можуть бути зареєстровані на тестових інтерфейсах,
високорівневих тестових класів, або тестових класів @Nested
.
Реєстрація
декількох розширень, що реалізують |
TestInstancePostProcessor
визначає API для Extensions
,
що бажає виконати пост-процесинг примірників тестів.
Загальні випадки використання включають ін'єкцію залежностей до примірників тестів, виклик власних методів ініціалізації на примірнику теста, etc.
Для
конкретного приклада дивіться джерельний код для MockitoExtension
та SpringExtension
.
ParameterResolver
визначає Extension
API
для динамічного розрішення параметрів під час виконання.
Якщо
конструктор тесту або метод @Test
,
@RepeatedTest
,
@ParameterizedTest
,
@TestFactory
,
@BeforeEach
,
@AfterEach
,
@BeforeAll
або @AfterAll
сприймає параметр, параметр має бути розрішений під
час виконання в ParameterResolver
.
ParameterResolver
може бути або вбудованим (дивіться TestInfoParameterResolver
),
або зареєстрований
користувачем. Загально кажучи, параметри можуть бути
розрішені за іменем, типом, анотацією, або любою їх
комбінацією. Для конкретних прикладів дивіться джерельний код для
CustomTypeParameterResolver
таCustomAnnotationParameterResolver
.
Через
баг в байткоді, згенерованому API
|
Наступні
інтерфейси визначають API для розширення тестів в різних точках
життєвого циклу виконання тесту. Радьтесь з наступними розділами
щодо прикладів, та звертайтесь до Javadoc для кожного з ціх
інтерфейсів в пакунку org.junit.jupiter.api.extension
для
подальших деталей.
Реалізація
декількох API розширень
Розробники розширень можуть обрати реалізувати любу
кількість ціх інтерфейсів в одному розширенні. Посилайтесь
до джерельного коду SpringExtension
для конкретних прикладів. |
BeforeTestExecutionCallback
та AfterTestExecutionCallback
визначають API для Extensions
,
що бажають додати поведінку, що буде виконана безпосередньо
перед та безпосередньо після виконання тесту,
відповідно. Як такі, ці зворотні виклики гарно пристосовані для
вимірів часу, трасування, та подібних випадків використання.
Якщо вам треба реалізовати зворотні виклики, що викликаються за
межами методів @BeforeEach
та @AfterEach
,
реалізуйте замість цього BeforeEachCallback
та AfterEachCallback
.
Наступний
приклад показує, як використовувати ці зворотні виклики для
обчислення та журналювання часу виконання тестового метода. TimingExtension
реалізує обоє, BeforeTestExecutionCallback
та AfterTestExecutionCallback
,
щоб заміряти час виконання тесту та записати його в журнал.
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 logger = Logger.getLogger(TimingExtension.class.getName());
private static final String START_TIME = "start time";
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
getStore(context).put(START_TIME, System.currentTimeMillis());
}
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
Method testMethod = context.getRequiredTestMethod();
long startTime = getStore(context).remove(START_TIME, long.class);
long duration = System.currentTimeMillis() - startTime;
logger.info(() -> String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));
}
}
Оскільки
класTimingExtensionTests
реєструє TimingExtension
через @ExtendWith
,
його тести будуть мати цей таймінг застосованим до тестів, коли
вони будуть виконуватись.
@ExtendWith(TimingExtension.class)
class TimingExtensionTests {
@Test
void sleep20ms() throws Exception {
Thread.sleep(20);
}
@Test
void sleep50ms() throws Exception {
Thread.sleep(50);
}
}
Далі
іде приклад журналу, що продуклюється при виконанні TimingExtensionTests
.
INFO: Method [sleep20ms] took 24 ms. INFO: Method [sleep50ms] took 53 ms.
TestExecutionExceptionHandler
визначає API для Extensions
,
що бажають обробляти підняння виключень під час виконання тестів.
Наступний
приклад показує розширення, що буде поглинати всі примірникі IOException
,
але перепідіймати всі інші тпи виключень.
public class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
throws Throwable {
if (throwable instanceof IOException) {
return;
}
throw throwable;
}
}
Метод
@TestTemplate
може бути виконаний, тільки якщо зареєстрований щонайменше один
TestTemplateInvocationContextProvider
.
Кожний такий провайдер відповідає за провадження Stream
з примірників TestTemplateInvocationContext
.
Кожний контекст може вказувати власне дисплейне ім'я та список
додаткових розширень, що будуть використані тільки для наступного
виклику метода @TestTemplate
.
Наступний
приклад показує, як писати тестовий шаблон, так само, як
реєструвати та реалізувати TestTemplateInvocationContextProvider
.
@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate(String parameter) {
assertEquals(3, parameter.length());
}
public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
return Stream.of(invocationContext("foo"), invocationContext("bar"));
}
private TestTemplateInvocationContext invocationContext(String parameter) {
return new TestTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return parameter;
}
@Override
public List<Extension> getAdditionalExtensions() {
return Collections.singletonList(new ParameterResolver() {
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) {
return parameterContext.getParameter().getType().equals(String.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) {
return parameter;
}
});
}
};
}
}
В
цьому прикладі тестовий шаблон буде викликаний два рази. Дисплейні
імена викликів будуть “foo” та “bar”, як вказано в контексті
виклику. Кожний виклик реєструє власний ParameterResolver
,
що використовується для розрішення параметра метода.
Вивід при використанні ConsoleLauncher
буде наступним.
└─ testTemplate(String) ✔ ├─ foo ✔ └─ bar ✔
API
розширення TestTemplateInvocationContextProvider
з початку призначене для реалізації різних типів тестів, що
покладаються на повторювані виклики тест-подібних методів, але в
різних контекстах — наприклад, з різними параметрами,
через підготовку примірника тестового классу в різний спосіб, або
декілька раз без модифікації контексту. Будь ласка посилайтесь до
реалізації Повторюваних
тестів або Параметризованих
тестів, що використовують це розширення, щоб
запровадити свою функціональність.
Зазвичай
розширення створюється тільки один раз. Так що постає запитання,
як зберігти стан від одного виклику розширення до іншого? ExtensionContext
API
провадить Store
саме для цієї цілі. Розширення можуть покладати значення
в сховище для подальшого отримання. Дивіться TimingExtension
щодо прикладу використання Store
з
областю рівня метода. Важливо пам'ятати, що значення, збережені в
ExtensionContext
під час виконання тесту не буде доступне в оточуючому ExtensionContext
.
Оскільки ExtensionContexts
можуть бути вкладеними, область вкладеного контексту
також може бути обмежена. Порадьтесь з відповідним JavaDoc щодо
деталей по методам, доступним для зберігання та зчитування значень
через Store
.
ExtensionContext.Store.CloseableResource CloseableResource ,
повідомляються через виклик їх метода close() . |
Артифакт
junit-platform-commons
показує пакунок на ім'я org.junit.platform.commons.support
,
що містить підтримувані методи-утиліти для роботи з
анотаціями, класами, рефлексією, та завданнями сканування
classpath. Автори TestEngine
та
Extension
заохочуються використовувати ці підтримувані методи, щоб
дотримуватись поведінки JUnit Platform.
AnnotationSupport
провадить статичні методи утиліт, що оперують на анотованих
елементах (пакунках, анотаціях, класах, інтерфейсах,
конструкторах, методах та полях). Це включає методи для
перевірки, чи елемент анотований, або мета-анотований певною
анотацією, для пошуку специфічних анотацій, та для пошуку
анотованих методів та полів в класі або інтерфейсі. Деякі з ціх
методів шукають по реалізованих інтерфейсах і в ієрархіях класів
в пошуках анотацій. Звертайтесь доJavaDoc щодо AnnotationSupport
для подальших деталей.
ClassSupport
провадить статичні методи утіліт для роботи з класами (примірниками
java.lang.Class
).
Звертайтесь до JavaDoc для ClassSupport
для подальших деталей.
ReflectionSupport
провадить статичні методи утиліт, що розширюють
стандартну рефлексію JDK та механізми завантаження класів. Це
включає методи для сканування classpath в пошуку класів, що
співпадають з вказаними предикатами, завантаження або
створення нових примірників класу, та пошук та виклик методів.
Деякі з ціх методів подорожують по ієрархіям класів, щоб знайти
співпадаючі методи. Звертайтесь до JavaDoc щодо ReflectionSupport
для подальших деталей.
При виконанні тестових класів, що містять один або більше тестових класів, що містять один або більше тестових методів, на додаток до користувацького кода та методів життєвого циклу викликаються декілька методів розширення. Наступна діаграмма ілюструє відносний порядок кода користувача та коду розширення.
Запроваджений користувачем тест та методи життєвого циклу показані помаранчевим, та код зворотніх викликів від розширень синім. Сірий блок позначає виконання методу окремого тесту, та буде повторюватись для кожного тестового метода в тестовому класі.
Наступна таблиця надалі пояснює дванадцять кроків, змальованих на диаграмі Код користувача та код розширення.
Крок | Інтерфейс/анотація | Опис |
---|---|---|
1 |
interface |
код розширення, що виконується перед усіма тестами в контейнері |
2 |
annotation |
код користувача, що виконується перед усіма тестами в контейнері |
3 |
interface |
код розширення, що виконується перед кожним тестомd |
4 |
annotation |
код користувача, що виконується перед кожним тестом |
5 |
interface |
код розширення, що виконується безпосередньо перед тестом |
6 |
annotation |
код користувача, що є власне кодом тестового метода |
7 |
interface |
код розширення для обробки виключень в тесті |
8 |
interface |
код розширення, що виконується безпосередньо після тесту та відповідних обробників виключень |
9 |
annotation |
код користувача, що виконується після кожного тесту |
10 |
interface |
код розширення, що виконується після кожного теста |
11 |
annotation |
код користувача, що виконується після всіх тестів в контейнері |
12 |
interface |
код розширення, що виконується після всіх тестів в контейнері |
В простішому випадку буде виконаний тільки саме тестовий метод (крок 6); всі інші кроки опціональні, в залежності від присутності користувацького коду та підтримки розширень для відповідних зворотніх викликів життєвого циклу. Для подальших деталей щодо викликів життєвого цикла будь ласк а звертайтесь до відповідних JavaDoc для кожних анотації та розширення.
Хоча
програмна модель та модель розширення JUnit Jupiter не будуть
природно підтримувати можливості JUnit 4, такі як Rules
та Runners
,
не очікується, що підтримка джерельного коду буде потребувати
оновлення всіх існуючих тестів, розширень тестів, та тестової
інфраструктури власної побудови, щоб мігрувати на JUnit Jupiter.
Замість
цього JUnit провадить м'яку міграцію через тестовий рушій JUnit
Vintage test engine, що дозволяє існуючим тестам на
основі JUnit 3 та JUnit 4 виконуватись з використанням
інфраструктури JUnit Platform. Оскільки всі класи та анотації,
специфічні до JUnit Jupiter, залишаються під новим пакунком org.junit.jupiter
,
мати обоє, JUnit 4 та JUnit Jupiter в classpath не призводить до
жодних конфліктів. Так що є безпечним підтримувати існуючі тести
JUnit 4 поряд з тестами JUnit Jupiter. Більше того, оскікльи команда
JUnit буде продовжувати провадити підтримку та полагодження вад для
базової лінії JUnit 4.x, розробники мають безліч часу для міграції
до JUnit Jupiter за власним розкладом.
Просто
переконайтесь, що артифакт junit-vintage-engine
знаходиться по шляху вашого тестового рантайма. В такому випадку
тести JUnit 3 та JUnit 4 будуть автоматично
підхоплюватись ланчером JUnit Platform.
Дивіться
приклад проекту в репозитарії junit5-samples
,
щоб з'ясувати, як це зроблене в Gradle та Maven.
Для
тестових класів або методів, що анотовані @Category
,
тестовий рушій JUnit
Vintage показує повністью клаліфікове ім'я
класу категорії, як тег відповідного ідентифікатора test.
Наприклад, якщо метод анотований за допомогою @Category(Example.class)
,
він буде помічений тегом "com.acme.Example"
.
Подібно до раннера Categories
в JUnit 4, ця інформація може бути використана
для фільтрації знайдених тестів перед виконанням (дивіться
Виконання
тестів щодо деталей).
На наступні речі ви маєте звернути увагу, коли переносите існуючі тести 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
;
дивіться наступний розділ для часткової підтримки правил.
Як зазначалось раніше, JUnit Jupiter не підтримує і не буде природно підтримувати правила JUnit 4. Однак команда JUnit уявляє, що багато організацій, особливо великіх, вірогідно мають велику кодову базу JUnit 4, що використовує власні правила. Щоб обслуговувати ці організації, та дозволити поступову міграцію, команда JUnit вирішила підтримувати вибіркові правила JUnit 4 дослівно в JUnit Jupiter. Ця підтримка базована на адаптерах, та обмежена до тих правил, що симантично сумісні до моделі розширення JUnit Jupiter, тобто тих, що не докорінно змінюють загальний потік виконання тесту.
Модуль
junit-jupiter-migrationsupport
з JUnit Jupiter наразі підтримує наступні три типиRule
,
включаючи субкласи ціх типів:
org.junit.rules.ExternalResource
(включаючи
org.junit.rules.TemporaryFolder
)
org.junit.rules.Verifier
(включаючи
org.junit.rules.ErrorCollector
)
org.junit.rules.ExpectedException
Як
і в JUnit 4, Rule-анотовані поля та методи також підтримуються.
Через використання ціх розширень рівня класу на тестовому класі
реалізації Rule
в
старому коді можуть бути залишені без змін, включаючи
твердження імпорту правил в JUnit 4.
Ця
обмежена форма підтримки Rule
може бути ввімкнена через анотацію рівня класа org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport
.
Ця анотація є композитною анотацією, що дозволяє
всі розширення підтримки міграції: VerifierSupport
, ExternalResourceSupport
,
and ExpectedExceptionSupport
.
Однак, якщо ви націлені розробляти нове розширення до JUnit 5, будь ласка, використовуйте нову модель розширення JUnit Jupiter замість базованої на правилах моделі JUnit 4.
Підтримка
JUnit 4 Rule
JUnit Jupiter наразі є експериментальною
можливістю. Дивіться таблицю Експериментальні
API щодо деталей. |
Одна з помітних цілей JUnit 5 є зробити інтерфейс між JUnit та його програмними клієнтами – інструментами побудови та IDE – більш потужним та стабільним. Ціль є розділити внутрощі пошуку та виконання тестів від всієї фільтрації та конфігурації, що потрібна ззовні.
JUnit
5 вводить концепцію Launcher
,
що може бути використаний для пошуку, фільтрації, та виконання
тестів. Більше того, бібліотеки третіх сторін – як Spock,
Cucumber, та FitNesse –
можуть підключатись в інфраструктуру ланчера JUnit Platform, через
провадження власного TestEngine
.
API
ланцера в модулі junit-platform-launcher
.
Приклад
споживача API ланчера є ConsoleLauncher
в проекті junit-platform-console
.
Введення пошуку тестів як окремої можливості самої платформи (маємо надію) вивільнить IDE та інструменти побудови від більшості складностей, які вони мали від ідентифікації тестових класів та тестових методів в минулому.
Приклад використання:
import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherConfig;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(
selectPackage("com.example.mytests"),
selectClass(MyTestClass.class)
)
.filters(
includeClassNamePatterns(".*Tests")
)
.build();
Launcher launcher = LauncherFactory.create();
TestPlan testPlan = launcher.discover(request);
Наразі існує можливість обирати класи, методи, та всі класи в пакунку, або навіть шукати всі тести по classpath. Пошук має місце серед всіх приймаючих участь тестових двигунів.
Отриманий
TestPlan
є ієрархічним (та тільки для читання) описом всіх рушіїв,
класів, та тестових методів, що задовільняють LauncherDiscoveryRequest
.
Клієнт може подорожувати цім деревом, отримувати деталі щодо
вузлів, та отримати посилання на оригінальне джерело (як клас,
метод, або розташування файла). Кожний вузол в тестовому плані
має унікальний
ID,
що може бути використаний для виклику окремого тесту або
групи тестів.
Щоб
виконувати тести, клієнти можуть використовувати той самий
LauncherDiscoveryRequest
,
що і на фазі пошуку, або створити новий запит. Прогрес тесту
та звітність можуть бути досягненіі через реєстрацію одного
або більше реалізацій TestExecutionListener
на Launcher
,
як в наступному прикладі.
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(
selectPackage("com.example.mytests"),
selectClass(MyTestClass.class)
)
.filters(
includeClassNamePatterns(".*Tests")
)
.build();
Launcher launcher = LauncherFactory.create();
// Реєструємо слухача за вашим бажанням
TestExecutionListener listener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
Немає
значення повернення для метода execute()
,
але ви можете легко використовувати слухача для агрегації
фінальних результатів у власному об'єкті. Для прикладу дивіться
SummaryGeneratingListener
.
JUnit
наразі провадить дві реалізації TestEngine
.
junit-jupiter-engine
:
Ядро JUnit Jupiter.
junit-vintage-engine
:
Тонкій прошарок нагорі JUnit 4, що дозволяє виконання вінтажних
тестів з інфраструктурою ланчера.
Треті
сторони також можуть докладати свої власні TestEngine
,
через реалізацію інтерфейсів в модулі junit-platform-engine
та реєстрацію своїх рушіїв. По замовчанню, реєстрація
рушія підтримується через механізм Java java.util.ServiceLoader
.
Наприклад, модуль the junit-jupiter-engine
реєструє свій org.junit.jupiter.engine.JupiterTestEngine
в файлі з назвою org.junit.platform.engine.TestEngine
в /META-INF/services
в junit-jupiter-engine
JAR.
HierarchicalTestEngine
є зручною абстрактною базовою реалізацією (що
використовоується junit-jupiter-engine )
, що лише вимагає від реалізаторів запровадити логіку для
пошуку тестів. Вона реалізує виконання TestDescriptors ,
що реалізує інтерфейс Node ,
включаючи підтримку для паралельного виконання. |
The
junit- prefix
is reserved for TestEngines from the JUnit TeamJUnit
Platform
|
На
додаток до публічного метода Launcher
API
для програмної реєстрації слухачів виконання тестів, по
замовчанню власні реалізації TestExecutionListener
будуть шукатись під час виконання через механізм Java
java.util.ServiceLoader
,
та автоматично реєструватись через Launcher
,
створений через LauncherFactory
.
Наприклад, клас example.TestInfoPrinter
реалізуючий TestExecutionListener
,
та декларований в файлі /META-INF/services/org.junit.platform.launcher.TestExecutionListener
,
завантажується та реєструється автоматично.
Якщо
ви потребуєте гарний контроль над автоматичною детекцією та
реєстрацією рушіїв та слухачів виконання, ви можете створити
примірник LauncherConfig
,
та надати його до методаLauncherFactory.create(LauncherConfig)
.
Типово примірник LauncherConfig
створюється через вбудований API
білдера, як демонструється в наступному прикладі.
LauncherConfig launcherConfig = LauncherConfig.builder()
.enableTestEngineAutoRegistration(false)
.enableTestExecutionListenerAutoRegistration(false)
.addTestEngines(new CustomTestEngine())
.addTestExecutionListeners(new CustomTestExecutionListener())
.build();
Launcher launcher = LauncherFactory.create(launcherConfig);
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectPackage("com.example.mytests"))
.build();
launcher.execute(request);
Одна з найбільших цілей JUnit 5 є покращення можливостей підтримки з розвитку JUnit, незважаючи на те, що він задіяний в багатьох проектах. В JUnit 4 було додано багато речей як внутрішні конструкції, що використовувались тільки дописувачами зовнішніх розширень та побудовниками інструментів. Це зробило зміну JUnit 4 особливо складною та іноді неможливою.
Ось чому JUnit 5 вводить виозначений життєвий цикл для всіх публічно доступних інтерфейсів, класів та методів.
Кожний
опублікований артифакт має номер версії <major>.<minor>.<patch>
,
та всі публічно доступні інтерфейси, класи та методи анотовані за
допомогою @API
з проекта @API
Guardian. Атрибуту анотації status
може бути присвоєне одне з наступних значень.
Статус | Опис |
---|---|
|
Не має використовувати будь-який код, крім самого JUnit itself. Може бути видалено без попереднього повідомлення. |
|
Більше не повинно використовуватись; може зникнути в наступному мінорному релізі. |
|
Призначено
для нових, експериментаньних можливостей, де очікується
зворотній зв'язок. Використовуйте цей елемент з осторогою;
він може бути пересунутий до |
|
Призначений
для можливостей, що не будуть змінені в зворотньо-несумісний
спосіб щонайменьше в наступному мінорному
релізі поточної мажорної версії. Якщо видалення заплановане,
це буде спочатку понижене до |
|
Призначено
для можливостей, що не будуть змінені в зворотньо-несумісний
спосіб в поточній мажорній версії ( |
Якщо
@API
анотація присутня для типа, це розглядається що це стосується
всіх публічних членів того самого типу, також. Члену дозволяється
декларувати інше значення status
з меньшою стабільністю.
Наступна
таблиця наводить перелік API, що наразі відмічені як експериментальними
через @API(status
= EXPERIMENTAL)
. При покладанні на ці API треба бути
обережним.
Ім'я пакунку | Ім'я типу | Починаючи |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Наступна
таблиця наводить перелік API, які наразі відмічені як застарілі
через @API(status
= DEPRECATED)
. Ви повинні уникати використання ціх
API за змоги, оскільки такі API будуть вірогідно видалені в
надходячому релізі.
Ім'я пакунку | Назва типу | Починаючи |
---|---|---|
|
|
|
|
|
|
Проект
@API
Guardian планує запровадити підтримку інструментації
для публікаторів та споживачів API, анотованих за
допомогою @API.
Наприклад, підтримка інструментації вірогідно буде провадити
перевірку, чи JUnit API були використані у відповідності з
деклараціями анотацій @API
.
Перегляньте поточний список контриб'юторів прямо на GitHub.
Зауваження щодо релізу доступні тут.