[JAVA] Verwendungshinweise zu JUnit5

Was ist JUnit5?

Selbstverständlich die neueste Hauptversion des Java-Testframeworks ab 2019.

Umgebung

> gradle --version
------------------------------------------------------------
Gradle 5.6.2
------------------------------------------------------------

Build time:   2019-09-05 16:13:54 UTC
Revision:     55a5e53d855db8fc7b0e494412fc624051a8e781

Kotlin:       1.3.41
Groovy:       2.5.4
Ant:          Apache Ant(TM) version 1.9.14 compiled on March 12 2019
JVM:          11.0.4 (AdoptOpenJDK 11.0.4+11)
OS:           Windows 10 10.0 amd64

Hello World

Implementierung

build.gradle


plugins {
    id "java"
}

sourceCompatibility = 11
targetCompatibility = 11
[compileJava, compileTestJava]*.options*.encoding = "UTF-8"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation "org.junit.jupiter:junit-jupiter:5.5.2"
}

--Wenn Sie JUnit5 vorerst verwenden möchten, geben Sie als Abhängigkeit org.junit.jupiter: junit-jupiter an ( Details unten ).

Dateiorganisation


|-build.gradle
`-src/test/java/
  `-sample/junit5/
    |-JUnit5Test.java
    |-JUnit5Tests.java
    |-TestJUnit5.java
    `-Hoge.java

――Wir haben 4 Arten von Testklassen

JUnit5Test.java


package sample.junit5;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;

class JUnit5Test {
    @Test
    void fail() {
        Assertions.assertEquals(10, 8);
    }

    static class StaticClass {
        @Test
        void fail() {
            Assertions.assertEquals(10, 8);
        }
    }

    static class StaticTest {
        @Test
        void fail() {
            Assertions.assertEquals(10, 8);
        }
    }

    class InnerTest {
        @Test
        void fail() {
            Assertions.assertEquals(10, 8);
        }
    }
}

Führen Sie in ConsoleLauncher aus

Ein Tool zum Ausführen von JUnit5 in der Befehlszeile ist ** ConsoleLauncher **. Die Substanz ist eine JAR-Datei, in der jedes Modul von JUnit5 in einem zusammengefasst ist. Mavens zentrales Repository Herunterladen.

Laden Sie hier "junit-platform-console-standalone-1.5.2.jar" herunter und überprüfen Sie sie.

#Kompilieren
> gradle compileTestJava

#Lauf
> java -jar junit-platform-console-standalone-1.5.2.jar ^
       -cp build\classes\java\test ^
       --scan-classpath build\classes\java\test

...

Failures (8):
  JUnit Jupiter:Hoge$StaticTest:fail()
    MethodSource [className = 'sample.junit5.Hoge$StaticTest', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:TestJUnit5:fail()
    MethodSource [className = 'sample.junit5.TestJUnit5', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:TestJUnit5$StaticTest:fail()
    MethodSource [className = 'sample.junit5.TestJUnit5$StaticTest', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:JUnit5Test:fail()
    MethodSource [className = 'sample.junit5.JUnit5Test', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:JUnit5Tests:fail()
    MethodSource [className = 'sample.junit5.JUnit5Tests', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:TestJUnit5$StaticClass:fail()
    MethodSource [className = 'sample.junit5.TestJUnit5$StaticClass', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:JUnit5Test$StaticTest:fail()
    MethodSource [className = 'sample.junit5.JUnit5Test$StaticTest', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
  JUnit Jupiter:JUnit5Tests$StaticTest:fail()
    MethodSource [className = 'sample.junit5.JUnit5Tests$StaticTest', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...

Test run finished after 91 ms
[        10 containers found      ]
[         0 containers skipped    ]
[        10 containers started    ]
[         0 containers aborted    ]
[        10 containers successful ]
[         0 containers failed     ]
[         8 tests found           ]
[         0 tests skipped         ]
[         8 tests started         ]
[         0 tests aborted         ]
[         0 tests successful      ]
[         8 tests failed          ]

Testklassen-Suchkriterien

> java -jar junit-platform-console-standalone-1.5.2.jar --help
...
  -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?)$]
...

Lauf von Gradle weg

Aber ich denke, es wird normalerweise mit dem Build-Tool gemacht, das Sie verwenden. Gradle bietet native Unterstützung für die Ausführung von JUnit5 seit 4.6. Versuchen Sie also, es von Gradle aus auszuführen.

build.gradle


...

test {
    useJUnitPlatform() //★ Ergänzung
}

--Gradle unterstützt auch andere Test-Frameworks wie JUni4 und TestNG

Führen Sie den Test aus


> gradle test
...

> Task :test FAILED

sample.junit5.Hoge > fail() FAILED
    org.opentest4j.AssertionFailedError at Hoge.java:10

sample.junit5.JUnit5Test > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Test.java:10

sample.junit5.JUnit5Tests$StaticTest > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Tests.java:23

sample.junit5.JUnit5Tests > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Tests.java:10

sample.junit5.TestJUnit5 > fail() FAILED
    org.opentest4j.AssertionFailedError at TestJUnit5.java:10

sample.junit5.Hoge$StaticClass > fail() FAILED
    org.opentest4j.AssertionFailedError at Hoge.java:16

sample.junit5.Hoge$StaticTest > fail() FAILED
    org.opentest4j.AssertionFailedError at Hoge.java:23

sample.junit5.JUnit5Test$StaticClass > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Test.java:16

sample.junit5.JUnit5Test$StaticTest > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Test.java:23

sample.junit5.JUnit5Tests$StaticClass > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Tests.java:16

sample.junit5.TestJUnit5$StaticClass > fail() FAILED
    org.opentest4j.AssertionFailedError at TestJUnit5.java:16

sample.junit5.TestJUnit5$StaticTest > fail() FAILED
    org.opentest4j.AssertionFailedError at TestJUnit5.java:23

12 tests completed, 12 failed
...

BUILD FAILED in 6s
2 actionable tasks: 1 executed, 1 up-to-date

Zielklasse bei Ausführung in Eclipse

Die bestätigte Version ist "2019-09 Standard Edition" von Pleiades.

Um es auszuführen, klicken Sie mit der rechten Maustaste auf den Ordner "src / test / java" und wählen Sie "Ausführen" -> "JUnit-Test".

junit5.jpg

"Hoge" und "StaticClass" wurden ebenfalls ins Visier genommen.

Interessante Klassen, wenn sie in IntelliJ IDEA ausgeführt werden

Die bestätigte Version ist die Community-Version 2019.2.3.

Wenn Sie IDEA als Gradle-Projekt öffnen, wird der Test von der Gradle-Testaufgabe ausgeführt. Das Verhalten in diesem Fall ist also das gleiche wie beim Ausführen von Gradle.

Sie können die Ausführungskonfiguration auch unter [Ausführen / Debuggen von Konfigurationen] angeben, ohne Gradle zu verwenden. In diesem Fall hängt es jedoch von der Spezifikation "Testart" ab.

junit5.jpg

die Architektur

junit5.jpg

JUnit5 besteht aus drei Hauptmodulen (Teilprojekten).

JUnit Platform

Die Basis für die Ausführung des Testframeworks auf der JVM. Es bietet einen Mechanismus zum Ausführen eines Moduls, das eine Schnittstelle namens [TestEngine] implementiert (https://junit.org/junit5/docs/current/api/org/junit/platform/engine/TestEngine.html).

Es bietet auch "ConsoleLauncher" usw. zum Starten von der Konsole.

JUnit Jupiter

Ein Modul zum Erstellen und Ausführen von JUnit 5-Tests. Bietet JupiterTestEngine, das "TestEngine" für JUnit 5 implementiert.

Sie können sich Jupiter als JUnit 5 vorstellen.

JUnit Vintage

Eine Klasse, die "TestEngine" implementiert, um JUnit 3 und 4 auf der JUnit-Plattform [VintageTestEngine] auszuführen (https://junit.org/junit5/docs/current/api/org/junit/vintage/engine/VintageTestEngine). Ein Modul, das HTML bereitstellt.

Ich denke, es wurde für die Übergangskompatibilität vorbereitet, daher ist es nicht erforderlich, wenn Sie eine neue JUnit 5 einführen.

Zusamenfassend

JUnit 5 besteht aus den folgenden zwei Modulen.

Zum Ausführen des Tests ist eine Plattform erforderlich. Jupiter wird benötigt, wenn Sie Tests in JUnit 5 schreiben möchten, und Vintage wird benötigt, wenn Sie JUnit 4 auf der JUnit-Plattform ausführen möchten.

Erforderliche Artefakte

Module wie Platform und Jupiter entsprechen der Gruppe in Mavens Klassifikation.

Innerhalb jeder Gruppe gibt es mehrere weitere Artefakte.

** Artefaktabhängigkeiten innerhalb jedes Moduls **

junit5.jpg

Wenn Sie tatsächlich einen JUnit 5-Test schreiben, müssen Sie nur diejenigen auswählen, die Sie aus diesen Artefakten benötigen, und sie Ihren Abhängigkeiten hinzufügen. Wie Sie der obigen Abbildung entnehmen können, ist es auf den ersten Blick schwierig zu bestimmen, welche benötigt wird.

Daher wurde in 5.4.0 ein Artefakt namens junit-jupiter zur Gruppe "org.junit.jupiter" hinzugefügt.

** Abbildung mit Junit-Jupiter hinzugefügt **

junit5.jpg

junit-jupiter ist ein Artefakt, das nur die Mindestabhängigkeiten zusammenfasst, die zum Schreiben von Tests in JUnit 5 erforderlich sind. Wenn Sie also nur Tests in JUnit 5 schreiben möchten, müssen Sie nur eines dieser Artefakte zu Ihren Abhängigkeiten hinzufügen.

Wie schreibe ich einen Test?

Testmethode

package sample.junit5;

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

class JUnit5Test {
    @Test
    void success() {
        Assertions.assertEquals(10, 10);
    }

    @Test
    void fail() {
        Assertions.assertEquals(10, 8);
    }
}

-Die von @Test kommentierte Methode wird zur Testmethode.

Vorverarbeitung / Nachbearbeitung

package sample.junit5;

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.Test;

class JUnit5Test {

    @BeforeAll
    static void beforeAll() {
        System.out.println("JUnit5Test#beforeAll()");
    }
    
    @BeforeEach
    void beforeEach() {
        System.out.println("  JUnit5Test#beforeEach()");
    }
    
    @Test
    void test1() {
        System.out.println("    JUnit5Test#test1()");
    }
    
    @Test
    void test2() {
        System.out.println("    JUnit5Test#test2()");
    }
    
    @AfterEach
    void afterEach() {
        System.out.println("  JUnit5Test#afterEach()");
    }
    
    @AfterAll
    static void afterAll() {
        System.out.println("JUnit5Test#afterAll()");
    }
}

Ausführungsergebnis


JUnit5Test#beforeAll()
  JUnit5Test#beforeEach()
    JUnit5Test#test1()
  JUnit5Test#afterEach()
  JUnit5Test#beforeEach()
    JUnit5Test#test2()
  JUnit5Test#afterEach()
JUnit5Test#afterAll()

-Methoden mit @BeforeAll stehen in der Testklasse nur einmal ganz am Anfang. Wird durchgeführt

Anzeigename

package sample.junit5;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;

@DisplayName("Im Unterricht")
class JUnit5Test {
    @Test
    @DisplayName("Erfolgreich sein")
    void success() {
        Assertions.assertEquals(10, 10);
    }

    @Test
    @DisplayName("Im Misserfolg")
    void fail() {
        Assertions.assertEquals(10, 8);
    }
}

** Bei Ausführung mit ConsoleLauncher **

> java -jar junit-platform-console-standalone-1.5.2.jar ^
       -cp build\classes\java\test ^
       --scan-classpath build\classes\java\test

...

.
+-- JUnit Jupiter [OK]
| '--Im Unterricht[OK]
|   +--Erfolgreich sein[OK]
|   '--Im Misserfolg[X] expected: <10> but was: <8>
'-- JUnit Vintage [OK]

Failures (1):
  JUnit Jupiter:Im Unterricht:Im Misserfolg
    MethodSource [className = 'sample.junit5.JUnit5Test', methodName = 'fail', methodParameterTypes = '']
    => org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
       ...
       [...]

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

** Wenn auf Gradle ausgeführt **

> gradle test
...
> Task :test FAILED

sample.junit5.JUnit5Test > fail() FAILED
    org.opentest4j.AssertionFailedError at JUnit5Test.java:18

2 tests completed, 1 failed

FAILURE: Build failed with an exception.

...

BUILD FAILED in 6s
2 actionable tasks: 1 executed, 1 up-to-date

** HTML-Bericht **

junit5.jpg

junit5.jpg

** XML-Bericht **

xml:TEST-sample.junit5.JUnit5Test.xml


<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="sample.junit5.JUnit5Test" tests="2" skipped="0" failures="1" errors="0" timestamp="2019-10-08T13:55:57" hostname="niconico" time="0.022">
  <properties/>
  <testcase name="success()" classname="sample.junit5.JUnit5Test" time="0.016"/>
  <testcase name="fail()" classname="sample.junit5.JUnit5Test" time="0.005">
    <failure message="org.opentest4j.AssertionFailedError: expected: &lt;10&gt; but was: &lt;8&gt;" type="org.opentest4j.AssertionFailedError">org.opentest4j.AssertionFailedError: expected: &lt;10&gt; but was: &lt;8&gt;
...

Verschachtelter Test

package sample.junit5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class JUnit5Test {
    
    @BeforeEach
    void beforeEach() {
        System.out.println("JUnit5Test.beforeEach()");
    }

    @Test
    void test1() {
        System.out.println("  JUnit5Test.test1()");
    }

    @Test
    void test2() {
        System.out.println("  JUnit5Test.test2()");
    }

    @AfterEach
    void afterEach() {
        System.out.println("JUnit5Test.afterEach()");
    }
    
    @Nested
    class NestedTest {

        @BeforeEach
        void beforeEach() {
            System.out.println("  NestedTest.beforeEach()");
        }

        @Test
        void test1() {
            System.out.println("    NestedTest.test1()");
        }

        @Test
        void test2() {
            System.out.println("    NestedTest.test2()");
        }

        @AfterEach
        void afterEach() {
            System.out.println("  NestedTest.afterEach()");
        }
    }
}

Ausführungsergebnis


JUnit5Test.beforeEach()
  JUnit5Test.test1()
JUnit5Test.afterEach()
JUnit5Test.beforeEach()
  JUnit5Test.test2()
JUnit5Test.afterEach()
JUnit5Test.beforeEach()
  NestedTest.beforeEach()
    NestedTest.test1()
  NestedTest.afterEach()
JUnit5Test.afterEach()
JUnit5Test.beforeEach()
  NestedTest.beforeEach()
    NestedTest.test2()
  NestedTest.afterEach()
JUnit5Test.afterEach()

[^ 2]: @ BeforeAll, @ AfterAll muss als statische Methoden angegeben werden, aber nicht statische innere Klassen können keine statischen Methoden gemäß den Java-Sprachspezifikationen definieren.

Geben Sie die Voraussetzungen an

package sample.junit5;

import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class JUnit5Test {

    @Test
    void test1() {
        Assumptions.assumeTrue(true);

        System.out.println("test1()");
    }

    @Test
    void test2() {
        Assumptions.assumeTrue(false);

        System.out.println("test2()");
    }

    @Test
    void test3() {
        Assumptions.assumingThat(true, () -> {
            System.out.println("test3() assumption.");
        });

        System.out.println("test3()");
    }

    @Test
    void test4() {
        Assumptions.assumingThat(false, () -> {
            System.out.println("test4() assumption.");
        });

        System.out.println("test4()");
    }
}

Ausführungsergebnis


test1()
test3() assumption.
test3()
test4()

...

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    +-- test1() [OK]
    +-- test2() [A] Assumption failed: assumption is not true
    +-- test3() [OK]
    '-- test4() [OK]

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

-Wenn Sie Assumptions.assumeTrue (boolean) verwenden, lautet das Argument ` Nachfolgende Tests werden nur ausgeführt, wenn Sie true bestehen --Wenn es "false" ist, wird der Rest der Verarbeitung in der Testmethode unterbrochen.

Test deaktivieren

package sample.junit5;

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

class JUnit5Test {

    @Test
    void test1() {
        System.out.println("test1()");
    }

    @Test
    @Disabled
    void test2() {
        System.out.println("test2()");
    }
}

Ausführungsergebnis


test1()

-Testmethoden mit @Disabled werden nicht mehr ausgeführt

Bedingter Test

OS

package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;

class JUnit5Test {

    @Test
    @EnabledOnOs(OS.WINDOWS)
    void test1() {
        System.out.println("enabled on windows");
    }

    @Test
    @EnabledOnOs(OS.MAC)
    void test2() {
        System.out.println("enabled on mac");
    }

    @Test
    @DisabledOnOs(OS.WINDOWS)
    void test3() {
        System.out.println("disabled on windows");
    }

    @Test
    @DisabledOnOs(OS.MAC)
    void test4() {
        System.out.println("disabled on mac");
    }
}

Ausführungsergebnis


enabled on windows
disabled on mac

Java-Version

package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.EnabledOnJre;
import org.junit.jupiter.api.condition.JRE;

class JUnit5Test {

    @Test
    @EnabledOnJre(JRE.JAVA_11)
    void test1() {
        System.out.println("enabled on java 11");
    }

    @Test
    @EnabledOnJre(JRE.JAVA_12)
    void test2() {
        System.out.println("enabled on java 12");
    }

    @Test
    @DisabledOnJre(JRE.JAVA_11)
    void test3() {
        System.out.println("disabled on java 11");
    }

    @Test
    @DisabledOnJre(JRE.JAVA_12)
    void test4() {
        System.out.println("disabled on java 12");
    }
}

Ausführungsergebnis


enabled on java 11
disabled on java 12

Systemeigenschaften

package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;

class JUnit5Test {
    
    @Test
    @EnabledIfSystemProperty(named = "java.vendor", matches = "AdoptOpenJDK")
    void test1() {
        System.out.println("enabled if AdoptOpenJDK");
    }

    @Test
    @EnabledIfSystemProperty(named = "java.vendor", matches = "Oracle.*")
    void test2() {
        System.out.println("enabled if Oracle");
    }

    @Test
    @DisabledIfSystemProperty(named = "java.vendor", matches = "AdoptOpenJDK")
    void test3() {
        System.out.println("disabled if AdoptOpenJDK");
    }

    @Test
    @DisabledIfSystemProperty(named = "java.vendor", matches = "Oracle.*")
    void test4() {
        System.out.println("disabled if Oracle");
    }
}

Ausführungsergebnis


enabled if AdoptOpenJDK
disabled if Oracle

-Wenn @EnabledIfSystemProperty hinzugefügt wird, wird der Test basierend auf dem Wert der Systemeigenschaft durchgeführt. Kann aktiviert werden -Wenn @DisabledIfSystemProperty hinzugefügt wird, wird der Test basierend auf dem Wert der Systemeigenschaft durchgeführt. Kann deaktiviert werden

Umgebungsvariable

package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;

class JUnit5Test {
    
    @Test
    @EnabledIfEnvironmentVariable(named = "JAVA_HOME", matches = ".*\\\\AdoptOpenJDK\\\\.*")
    void test1() {
        System.out.println("enabled if AdoptOpenJDK");
    }

    @Test
    @EnabledIfEnvironmentVariable(named = "JAVA_HOME", matches = ".*\\\\OpenJDK\\\\.*")
    void test2() {
        System.out.println("enabled if OpenJDK");
    }

    @Test
    @DisabledIfEnvironmentVariable(named = "JAVA_HOME", matches = ".*\\\\AdoptOpenJDK\\\\.*")
    void test3() {
        System.out.println("disabled if AdoptOpenJDK");
    }

    @Test
    @DisabledIfEnvironmentVariable(named = "JAVA_HOME", matches = ".*\\\\OpenJDK\\\\.*")
    void test4() {
        System.out.println("disabled if OpenJDK");
    }
}

Ausführungsergebnis


enabled if AdoptOpenJDK
disabled if OpenJDK

-Wenn @EnabledIfEnvironmentVariable hinzugefügt wird, wird der Test basierend auf dem Wert der Umgebungsvariablen durchgeführt. Kann aktiviert werden -Wenn @DisabledIfEnvironmentVariable hinzugefügt wird, wird der Test basierend auf dem Wert der Umgebungsvariablen durchgeführt. Kann deaktiviert werden

Tag-Filterung

package sample.junit5;

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

class JUnit5Test {

    @Test
    @Tag("foo")
    @Tag("fizz")
    void test1() {
        System.out.println("test1@(foo, fizz)");
    }
    
    @Test
    @Tag("bar")
    @Tag("fizz")
    void test2() {
        System.out.println("test2@(bar, fizz)");
    }

    @Test
    @Tag("fizz")
    void test3() {
        System.out.println("test3@(fizz)");
    }

    @Test
    @Tag("buzz")
    void test4() {
        System.out.println("test4@(buzz)");
    }
}

** Bei normaler Ausführung **

Ausführungsergebnis



> java -jar junit-platform-console-standalone-1.5.2.jar ^
       -cp build\classes\java\test ^
       --scan-classpath build\classes\java\test ^
       -e junit-jupiter

...
test1@(foo, fizz)
test2@(bar, fizz)
test3@(fizz)
test4@(buzz)

** - Beim Eingrenzen durch Include-Tag **

Ausführungsergebnis


> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "fizz"
...
test1@(foo, fizz)
test2@(bar, fizz)
test3@(fizz)

> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "foo & fizz"
...
test1@(foo, fizz)

> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "foo | bar"
...
test1@(foo, fizz)
test2@(bar, fizz)

> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "!foo & fizz"
...
test2@(bar, fizz)
test3@(fizz)

> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "foo | !fizz"
...
test1@(foo, fizz)
test4@(buzz)

junit5.jpg

build.gradle


...
test {
    useJUnitPlatform {
        includeTags "foo | !fizz"
    }
}

Ausführungsergebnis


> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "(!foo & fizz) | buzz"
...
test2@(bar, fizz)
test3@(fizz)
test4@(buzz)

Testausführungsreihenfolge

package sample.junit5;

import org.junit.jupiter.api.Test;

class JUnit5Test {

    @Test
    void bear() {
        System.out.println("bear");
    }

    @Test
    void ant() {
        System.out.println("ant");
    }

    @Test
    void cat() {
        System.out.println("cat");
    }

    @Test
    void dog() {
        System.out.println("dog");
    }
}

Ausführungsergebnis


ant
cat
dog
bear

Standardmäßig wird die Ausführungsreihenfolge von Testmethoden absichtlich durch einen nicht trivialen Algorithmus bestimmt [^ 1]. Dies liegt daran, dass es wünschenswert ist, dass Komponententests nicht von der Ausführungsreihenfolge abhängen.

[^ 1]: Kurz gesagt, ich denke, es ist eine Reihenfolge, die nicht zufällig ist, aber nicht erraten werden kann.

Bei Integrationstests und Funktionstests kann jedoch die Ausführungsreihenfolge wichtig sein. [^ 8]

[^ 8]: Verschieben Sie nach der Registrierung als Master einzelne Funktionen (wahrscheinlich)

Zu solchen Zeiten wird ein Mechanismus bereitgestellt, um die Testausführungsreihenfolge zu steuern.

Alphabetischer Reihenfolge

package sample.junit5;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

@TestMethodOrder(MethodOrderer.Alphanumeric.class)
class JUnit5Test {

    @Test
    void bear() {
        System.out.println("bear");
    }

    @Test
    void ant() {
        System.out.println("ant");
    }

    @Test
    void cat() {
        System.out.println("cat");
    }

    @Test
    void dog() {
        System.out.println("dog");
    }
}

Ausführungsergebnis


ant
bear
cat
dog

Angegeben durch Auftragsanmerkung

package sample.junit5;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class JUnit5Test {

    @Test
    @Order(3)
    void bear() {
        System.out.println("bear");
    }

    @Test
    @Order(4)
    void ant() {
        System.out.println("ant");
    }

    @Test
    @Order(2)
    void cat() {
        System.out.println("cat");
    }

    @Test
    @Order(1)
    void dog() {
        System.out.println("dog");
    }
}

Ausführungsergebnis


dog
cat
bear
ant

zufällig

package sample.junit5;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

@TestMethodOrder(MethodOrderer.Random.class)
class JUnit5Test {

    @Test
    void bear() {
        System.out.println("bear");
    }

    @Test
    void ant() {
        System.out.println("ant");
    }

    @Test
    void cat() {
        System.out.println("cat");
    }

    @Test
    void dog() {
        System.out.println("dog");
    }
}

Ausführungsergebnis


#1. Mal
ant
dog
cat
bear

#Zweites Mal
bear
ant
cat
dog

-Wenn Zufällig angegeben ist, ist die Ausführungsreihenfolge der Methoden zufällig.

Testinstanz-Lebenszyklus

Standardlebenszyklus

package sample.junit5;

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

class JUnit5Test {

    @BeforeEach
    void before() {
        System.out.println("before@" + this.hashCode());
    }
    
    @Test
    void test1() {
        System.out.println("test1@" + this.hashCode());
    }

    @Test
    void test2() {
        System.out.println("test2@" + this.hashCode());
    }

    @Test
    void test3() {
        System.out.println("test3@" + this.hashCode());
    }
}

Ausführungsergebnis


before@278240974
test1@278240974
before@370370379
test2@370370379
before@671046933
test3@671046933

Ändern Sie den Lebenszyklus für jede Testklasse

package sample.junit5;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

@TestInstance(TestInstance.Lifecycle.PER_CLASS) // ★
class JUnit5Test {
    
    @BeforeEach
    void before() {
        System.out.println("before@" + this.hashCode());
    }
    
    @Test
    void test1() {
        System.out.println("test1@" + this.hashCode());
    }

    @Test
    void test2() {
        System.out.println("test2@" + this.hashCode());
    }

    @Test
    void test3() {
        System.out.println("test3@" + this.hashCode());
    }
}

Ausführungsergebnis


before@1504642150
test1@1504642150
before@1504642150
test2@1504642150
before@1504642150
test3@1504642150
package sample.junit5;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class JUnit5Test {
    
    @BeforeAll
    static void staticBeforeAll() {
        System.out.println("staticBeforeAll()");
    }
    
    @BeforeAll
    void beforeAll() {
        System.out.println("beforeAll()");
    }

    @Test
    void test() {
        System.out.println("test()");
    }
    
    @AfterAll
    void afterAll() {
        System.out.println("afterAll()");
    }
    
    @AfterAll
    static void staticAfterAll() {
        System.out.println("staticAfterAll()");
    }
}

Ausführungsergebnis


beforeAll()
staticBeforeAll()
test()
staticAfterAll()
afterAll()
package sample.junit5;

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.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

class JUnit5Test {
    
    @BeforeAll
    static void beforeAll() {
        System.out.println("JUnit5Test.beforeAll()");
    }
    
    @BeforeEach
    void beforeEach() {
        System.out.println("  JUnit5Test.beforeEach()");
    }

    @Test
    void test1() {
        System.out.println("    JUnit5Test.test1()");
    }

    @Test
    void test2() {
        System.out.println("    JUnit5Test.test2()");
    }

    @AfterEach
    void afterEach() {
        System.out.println("  JUnit5Test.afterEach()");
    }
    
    @AfterAll
    static void afterAll() {
        System.out.println("JUnit5Test.afterAll()");
    }
    
    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    class NestedTest {

        @BeforeAll
        void beforeAll() {
            System.out.println(" NestedTest.beforeAll() *");
        }

        @BeforeEach
        void beforeEach() {
            System.out.println("   NestedTest.beforeEach()");
        }

        @Test
        void test1() {
            System.out.println("     NestedTest.test1()");
        }

        @Test
        void test2() {
            System.out.println("     NestedTest.test2()");
        }

        @AfterEach
        void afterEach() {
            System.out.println("   NestedTest.afterEach()");
        }

        @AfterAll
        void afterAll() {
            System.out.println(" NestedTest.afterAll() *");
        }
    }
}

Ausführungsergebnis


JUnit5Test.beforeAll()
  JUnit5Test.beforeEach()
    JUnit5Test.test1()
  JUnit5Test.afterEach()
  JUnit5Test.beforeEach()
    JUnit5Test.test2()
  JUnit5Test.afterEach()
 NestedTest.beforeAll() *
  JUnit5Test.beforeEach()
   NestedTest.beforeEach()
     NestedTest.test1()
   NestedTest.afterEach()
  JUnit5Test.afterEach()
  JUnit5Test.beforeEach()
   NestedTest.beforeEach()
     NestedTest.test2()
   NestedTest.afterEach()
  JUnit5Test.afterEach()
 NestedTest.afterAll() *
JUnit5Test.afterAll()

Ändern Sie den Standardlebenszyklus

Wiederholter Test

package sample.junit5;

import org.junit.jupiter.api.RepeatedTest;

class JUnit5Test {

    @RepeatedTest(3)
    void test() {
        System.out.println("test");
    }
}

** Ausführungsergebnis (für ConsoleLauncher) **

Ausführungsergebnis(ConsoleLauncher)


test
test
test

...

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    '-- test() [OK]
      +-- repetition 1 of 3 [OK]
      +-- repetition 2 of 3 [OK]
      '-- repetition 3 of 3 [OK]

Test run finished after 98 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          ]

** Ausführungsergebnis (für Gradle) **

junit5.jpg

Geben Sie den Anzeigenamen an

package sample.junit5;

import org.junit.jupiter.api.RepeatedTest;

class JUnit5Test {

    @RepeatedTest(
        name = "displayName={displayName}, currentRepetition={currentRepetition}, totalRepetitions={totalRepetitions}",
        value = 3
    )
    void test() {
        System.out.println("test");
    }
}

** Ausführungsergebnis (für Console Launcher) **

Ausführungsergebnis(ConsoleLauncher)


.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    '-- test() [OK]
      +-- displayName=test(), currentRepetition=1, totalRepetitions=3 [OK]
      +-- displayName=test(), currentRepetition=2, totalRepetitions=3 [OK]
      '-- displayName=test(), currentRepetition=3, totalRepetitions=3 [OK]

** Ausführungsergebnis (für Gradle) **

junit5.jpg

Erhalten Sie sich wiederholende Informationen mit der Testmethode

package sample.junit5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;

class JUnit5Test {
    
    @BeforeEach
    void before(RepetitionInfo repetitionInfo) {
        printRepetitionInfo("before", repetitionInfo);
    }

    @RepeatedTest(3)
    void test(RepetitionInfo repetitionInfo) {
        printRepetitionInfo("  test", repetitionInfo);
    }

    @AfterEach
    void after(RepetitionInfo repetitionInfo) {
        printRepetitionInfo("after", repetitionInfo);
    }
    
    private void printRepetitionInfo(String method, RepetitionInfo repetitionInfo) {
        int currentRepetition = repetitionInfo.getCurrentRepetition();
        int totalRepetitions = repetitionInfo.getTotalRepetitions();

        System.out.printf("%s (%d/%d)%n", method, currentRepetition, totalRepetitions);
    }
}

Ausführungsergebnis


before (1/3)
  test (1/3)
after (1/3)
before (2/3)
  test (2/3)
after (2/3)
before (3/3)
  test (3/3)
after (3/3)

Parametrisierter Test

package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @ValueSource(strings = {"hoge", "fuga", "piyo"})
    void test(String value) {
        System.out.println("value=" + value);
    }
}

** Ausführungsergebnis (für Console Launcher) **

Ausführungsergebnis(ConsoleLauncher)


value=hoge
value=fuga
value=piyo

Thanks for using JUnit! Support its development at https://junit.org/sponsoring

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    '-- test(String) [OK]
      +-- [1] hoge [OK]
      +-- [2] fuga [OK]
      '-- [3] piyo [OK]

Test run finished after 116 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          ]

** Ausführungsergebnis (für Gradle) **

junit5.jpg

-Die von @ParameterizedTest kommentierte Methode erhält den im Test verwendeten Wert als Argument. Wird ausgeführt während

Source Enum

package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @EnumSource(TestEnum.class)
    void test(TestEnum value) {
        System.out.println("value=" + value);
    }
    
    enum TestEnum {
        HOGE, FUGA, PIYO
    }
}

Ausführungsergebnis


value=HOGE
value=FUGA
value=PIYO

Verwenden Sie nur bestimmte Konstanten

package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @EnumSource(value = TestEnum.class , names = {"HOGE", "PIYO"})
    void test(TestEnum value) {
        System.out.println("value=" + value);
    }
    
    enum TestEnum {
        HOGE, FUGA, PIYO
    }
}

Ausführungsergebnis


value=HOGE
value=PIYO

-Sie können die in [Namen] verwendeten Konstanten eingrenzen (https://junit.org/junit5/docs/current/api/org/junit/jupiter/params/provider/EnumSource.html#names ())

Schließen Sie bestimmte Konstanten aus

package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @EnumSource(value = TestEnum.class, mode = EnumSource.Mode.EXCLUDE , names = {"HOGE", "PIYO"})
    void test(TestEnum value) {
        System.out.println("value=" + value);
    }
    
    enum TestEnum {
        HOGE, FUGA, PIYO
    }
}

Ausführungsergebnis


value=FUGA

-In Modus wird diese Bedingung durch names angegeben Sie können angeben, ob Sie sich bewerben möchten -Wenn EXCLUDE angegeben ist, geben Sie es mit names an. Ist ausgeschlossen

Geben Sie mit einem regulären Ausdruck an

package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @EnumSource(
        value = TestEnum.class,
        mode = EnumSource.Mode.MATCH_ALL,
        names = {"^F.*", ".*H$"}
    )
    void matchAll(TestEnum value) {
        System.out.println("matchAll() value=" + value);
    }

    @ParameterizedTest
    @EnumSource(
            value = TestEnum.class,
            mode = EnumSource.Mode.MATCH_ANY,
            names = {"^F.*", ".*H$"}
    )
    void matchAny(TestEnum value) {
        System.out.println("matchAny() value=" + value);
    }
    
    enum TestEnum {
        FIRST, SECOND, THIRD, FOURTH, FIFTH, SIXTH
    }
}

Ausführungsergebnis


matchAll() value=FOURTH
matchAll() value=FIFTH
matchAny() value=FIRST
matchAny() value=FOURTH
matchAny() value=FIFTH
matchAny() value=SIXTH

Quellmethode

package sample.junit5;

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

import java.util.List;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource
    void test(String value) {
        System.out.println("value=" + value);
    }

    static List<String> test() {
        return List.of("hoge", "fuga", "piyo");
    }
}

Ausführungsergebnis


value=hoge
value=fuga
value=piyo

Geben Sie die Quellmethode an

package sample.junit5;

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

import java.util.List;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource("sourceMethod")
    void test(String value) {
        System.out.println("value=" + value);
    }

    static List<String> sourceMethod() {
        return List.of("HOGE", "FUGA", "PIYO");
    }
    
    static List<String> test() {
        return List.of("hoge", "fuga", "piyo");
    }
}

Ausführungsergebnis


value=HOGE
value=FUGA
value=PIYO

Quellmethoden aus anderen Klassen

SourceClass


package sample.junit5;

import java.util.List;

class SourceClass {
    static List<String> createSource() {
        return List.of("foo", "bar");
    }
}

JUnit5Test


package sample.junit5;

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

import java.util.List;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource("sample.junit5.SourceClass#createSource")
    void test(String value) {
        System.out.println("value=" + value);
    }
}

Ausführungsergebnis


value=foo
value=bar

--In value of @ MethodSource können Sie die Methode der externen Klasse als Quelle verwenden, indem Sie den vollständig qualifizierten Namen der Klasse #method name` angeben.

Übergeben Sie mehrere formale Argumente mit einem Parameter

package sample.junit5;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.List;

import static org.junit.jupiter.params.provider.Arguments.*;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource
    void test1(String string, int i, boolean bool) {
        System.out.printf("test1() string=%s, i=%d, bool=%s%n", string, i, bool);
    }

    static List<Object[]> test1() {
        return List.of(
            new Object[]{"hoge", 11, false},
            new Object[]{"fuga", 17, true},
            new Object[]{"piyo", 19, true}
        );
    }

    @ParameterizedTest
    @MethodSource
    void test2(String string, int i, boolean bool) {
        System.out.printf("test2() string=%s, i=%d, bool=%s%n", string, i, bool);
    }

    static List<Arguments> test2() {
        return List.of(
            arguments("HOGE", 20, true),
            arguments("FUGA", 23, false),
            arguments("PIYO", 28, true)
        );
    }
}

Ausführungsergebnis


test1() string=hoge, i=11, bool=false
test1() string=fuga, i=17, bool=true
test1() string=piyo, i=19, bool=true
test2() string=HOGE, i=20, bool=true
test2() string=FUGA, i=23, bool=false
test2() string=PIYO, i=28, bool=true

CSV-Quelltext

package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @CsvSource({"foo,bar,1", "'hoge,fuga','',2", "fizz,,3"})
    void test(String s1, String s2, int i) {
        System.out.printf("s1=[%s], s2=[%s], i=[%d]%n", s1, s2, i);
    }
}

Ausführungsergebnis


s1=[foo], s2=[bar], i=[1]
s1=[hoge,fuga], s2=[], i=[2]
s1=[fizz], s2=[null], i=[3]

CSV-Quelldatei

Ordner Tributsteuer


`-src/test/
  |-resources/
  | `-test.csv
  `-java/
    `-sample/junit5/
      `-JUnit5Test.java

test.csv


hoge,1
fuga,2
piyo,3

JUnit5Test


package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @CsvFileSource(resources = "/test.csv")
    void test(String string, int i) {
        System.out.printf("string=[%s], i=[%d]%n", string, i);
    }
}

Ausführungsergebnis


string=[hoge], i=[1]
string=[fuga], i=[2]
string=[piyo], i=[3]

Erstellen Sie eine wiederverwendbare Quellklasse

MyArgumentsProvider


package sample.junit5;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;

import java.util.stream.Stream;

import static org.junit.jupiter.params.provider.Arguments.*;

public class MyArgumentsProvider implements ArgumentsProvider {

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
        return Stream.of(
            arguments("hoge", 1),
            arguments("fuga", 2),
            arguments("piyo", 3)
        );
    }
}

JUnit5Test


package sample.junit5;

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

class JUnit5Test {
    
    @ParameterizedTest
    @ArgumentsSource(MyArgumentsProvider.class)
    void test(String string, int i) {
        System.out.printf("string=[%s], i=[%d]%n", string, i);
    }
}

Ausführungsergebnis


string=[hoge], i=[1]
string=[fuga], i=[2]
string=[piyo], i=[3]

Parameterkonvertierung

Erweiterungskonvertierung

package sample.junit5;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.List;

import static org.junit.jupiter.params.provider.Arguments.*;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource
    void test(int i, long l, double d) {
        System.out.printf("i=%s, l=%s, d=%s%n", i, l, d);
    }
    
    static List<Arguments> test() {
        return List.of(arguments(10, 20, 30));
    }
}

Ausführungsergebnis


i=10, l=20, d=30.0

Implizite Konvertierung

package sample.junit5;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.File;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.List;

import static org.junit.jupiter.params.provider.Arguments.*;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource
    void test(
        boolean bool, char c, double d, TestEnum e, File file,
        Class<?> clazz, BigDecimal bd, Charset charset, LocalDateTime dateTime
    ) {
        System.out.printf(
            "bool=%s, c=%s, d=%s, e=%s, file=%s, clazz=%s, bd=%s, charset=%s, dateTime=%s%n",
            bool, c, d, e, file, clazz, bd, charset, dateTime
        );
    }
    
    static List<Arguments> test() {
        return List.of(
            arguments(
                "true", "c", "12.34", "FOO", "path/to/file",
                "java.lang.String", "98.76", "MS932", "2019-10-01T12:34:56"
            )
        );
    }
    
    enum TestEnum {
        FOO, BAR
    }
}

Ausführungsergebnis


bool=true, c=c, d=12.34, e=FOO, file=path\to\file, clazz=class java.lang.String, bd=98.76, charset=windows-31j, dateTime=2019-10-01T12:34:56
Typen und Konvertierungsmethoden, die die implizite Typkonvertierung unterstützen
Konvertierter Typ Konvertierungsmethode
boolean/java.lang.Boolean Boolean.valueOf(String)[^4]
char/java.lang.Character String.charAt(0)[^3]
byte/java.lang.Byte Byte.decode(String)
short/java.lang.Short Short.decode(String)
int/java.lang.Integer Integer.decode(String)
long/java.lang.Long Long.decode(String)
float/java.lang.Float Float.valueOf(String)
double/java.lang.Double Double.valueOf(String)
irgendeinenumArt (java.lang.EnumUnterklasse) Enum.valueOf(Class, String)
java.time.Duration Duration.parse(CharSequence)
java.time.Instant Instant.parse(CharSequence)
java.time.LocalDate LocalDate.parse(CharSequence)
java.time.LocalDateTime LocalDateTime.parse(CharSequence)
java.time.LocalTime LocalTime.parse(CharSequence)
java.time.MonthDay MonthDay.parse(CharSequence)
java.time.OffsetDateTime OffsetDateTime.parse(CharSequence)
java.time.OffsetTime OffsetTime.parse(CharSequence)
java.time.Period Period.parse(CharSequence)
java.time.Year Year.parse(CharSequence)
java.time.YearMonth YearMonth.parse(CharSequence)
java.time.ZonedDateTime ZonedDateTime.parse(CharSequence)
java.time.ZoneId ZoneId.of(String)
java.time.ZoneOffset ZoneOffset.of(String)
java.io.File new File(String)
java.nio.charset.Charset Charset.forName(String)
java.nio.file.Path Paths.get(String, String...)
java.net.URI URI.create(String)
java.net.URL new URL(String)
java.math.BigDecimal new BigDecimal(String)
java.math.BigInteger new BigInteger(String)
java.util.Currency Currency.getInstance(String)
java.util.Locale new Locale(String)
java.util.UUID UUID.fromString(String)
java.lang.Class * Details werden später beschrieben

[^ 3]: Fehler, wenn length () des ursprünglichen String nicht 1 ist [^ 4]: null ist ein Fehler

Konvertierung von java.lang.Class
package sample.junit5;

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

import java.util.Arrays;
import java.util.List;

class JUnit5Test {
    
    @ParameterizedTest
    @MethodSource
    void test(Class<?> clazz) {
        System.out.println(clazz);
    }
    
    static List<String> test() {
        return Arrays.asList(
            "int",
            "int[]",
            "int[][]",
            "[I",
            "[[I",
            "java.lang.String",
            "java.lang.String[]",
            "java.lang.String[][]",
            "[Ljava.lang.String;",
            "[[Ljava.lang.String;",
            "sample.junit5.JUnit5Test",
            "sample.junit5.JUnit5Test$InnerClass"
        );
    }
    
    class InnerClass {}
}

Ausführungsergebnis


int
class [I
class [[I
class [I
class [[I
class java.lang.String
class [Ljava.lang.String;
class [[Ljava.lang.String;
class [Ljava.lang.String;
class [[Ljava.lang.String;
class sample.junit5.JUnit5Test
class sample.junit5.JUnit5Test$InnerClass

--Primitive Typen können den Typnamen so angeben, wie er ist ("int", "long", "float" usw.)

Dynamischer Test

package sample.junit5;

import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.TestFactory;

import java.util.List;

import static org.junit.jupiter.api.DynamicTest.*;

class JUnit5Test {
    
    @TestFactory
    List<DynamicNode> testFactory() {
        return List.of(
            dynamicTest("Hoge", () -> System.out.println("Dynamic Hoge!!")),
            dynamicTest("Fuga", () -> System.out.println("Dynamic Fuga!!"))
        );
    }
}

Ausführungsergebnis


Dynamic Hoge!!
Dynamic Fuga!!

...
.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    '-- testFactory() [OK]
      +-- Hoge [OK]
      '-- Fuga [OK]

Dynamischer Testlebenszyklus

package sample.junit5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;

import java.util.List;

import static org.junit.jupiter.api.DynamicTest.*;

class JUnit5Test {
    
    @BeforeEach
    void beforeEach() {
        System.out.println("beforeEach()");
    }
    
    @TestFactory
    List<DynamicNode> testFactory() {
        System.out.println("  testFactory()");
        return List.of(
            dynamicTest("Hoge", () -> System.out.println("    Dynamic Hoge!!")),
            dynamicTest("Fuga", () -> System.out.println("    Dynamic Fuga!!"))
        );
    }
    
    @Test
    void test() {
        System.out.println("  test()");
    }
    
    @AfterEach
    void afterEach() {
        System.out.println("afterEach()");
    }
}

Ausführungsergebnis


beforeEach()
  testFactory()
    Dynamic Hoge!!
    Dynamic Fuga!!
afterEach()
beforeEach()
  test()
afterEach()

-- @ BeforeEach und @ AfterEach werden nur vor und nach der Methode ausgeführt, in der @ TestFactory eingestellt ist, nicht vor und nach jedem dynamischen Test.

Dynamische Tests verschachteln

package sample.junit5;

import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.TestFactory;

import java.util.List;

import static org.junit.jupiter.api.DynamicContainer.*;
import static org.junit.jupiter.api.DynamicTest.*;

class JUnit5Test {
    
    @TestFactory
    List<DynamicNode> testFactory() {
        return List.of(
            dynamicContainer("Dynamic Container 1", List.of(
                dynamicTest("Foo", () -> System.out.println("Dynamic Foo.")),
                dynamicContainer("Dynamic Container 1-1", List.of(
                    dynamicTest("Hoge", () -> System.out.println("Dynamic Hoge.")),
                    dynamicTest("Fuga", () -> System.out.println("Dynamic Fuga."))
                ))
            )),
            dynamicContainer("Dynamic Container 2", List.of(
                dynamicTest("Fizz", () -> System.out.println("Dynamic Fizz.")),
                dynamicTest("Buzz", () -> System.out.println("Dynamic Buzz."))
            ))
        );
    }
}

Ausführungsergebnis


Dynamic Foo.
Dynamic Hoge.
Dynamic Fuga.
Dynamic Fizz.
Dynamic Buzz.

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    '-- testFactory() [OK]
      +-- Dynamic Container 1 [OK]
      | +-- Foo [OK]
      | '-- Dynamic Container 1-1 [OK]
      |   +-- Hoge [OK]
      |   '-- Fuga [OK]
      '-- Dynamic Container 2 [OK]
        +-- Fizz [OK]
        '-- Buzz [OK]

Parallele Ausführung

junit-platform.properties


junit.jupiter.execution.parallel.enabled=true

--junit-platform.properties wird im Klassenpfad root platziert

package sample.junit5;

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.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

@Execution(ExecutionMode.CONCURRENT) //★ Vergiss nicht, das anzuziehen!
class JUnit5Test {
    @BeforeAll
    static void beforeAll() {
        printThread("beforeAll()");
    }

    @BeforeEach
    void beforeEach(TestInfo testInfo) {
        String name = testInfo.getDisplayName();
        printThread("  " + name + ":beforeEach()");
    }
    
    @Test
    void test1() {
        printThread("    test1()");
    }

    @Test
    void test2() {
        printThread("    test2()");
    }

    @AfterEach
    void afterEach(TestInfo testInfo) {
        String name = testInfo.getDisplayName();
        printThread("  " + name + ":afterEach()");
    }
    
    @AfterAll
    static void afterAll() {
        printThread("afterAll()");
    }
    
    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

Ausführungsergebnis


beforeAll()@ForkJoinPool-1-worker-3
  test1():beforeEach()@ForkJoinPool-1-worker-5
  test2():beforeEach()@ForkJoinPool-1-worker-7
    test2()@ForkJoinPool-1-worker-7
    test1()@ForkJoinPool-1-worker-5
  test1():afterEach()@ForkJoinPool-1-worker-5
  test2():afterEach()@ForkJoinPool-1-worker-7
afterAll()@ForkJoinPool-1-worker-3

Ändern Sie den Standardausführungsmodus

junit-platform.properties


junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
package sample.junit5;

import org.junit.jupiter.api.Test;

class JUnit5Test {
    
    @Test
    void test1() {
        printThread("test1()");
    }

    @Test
    void test2() {
        printThread("test2()");
    }
    
    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

Ausführungsergebnis


test1()@ForkJoinPool-1-worker-5
test2()@ForkJoinPool-1-worker-7

Ausnahmen, für die der Standardausführungsmodus nicht gilt

Beispiel für eine nicht parallele Ausführung, auch wenn der Standardausführungsmodus geändert wurde


package sample.junit5;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestMethodOrder;

class JUnit5Test {

    @Nested
    class StandardNestedTest {

        @Test
        void test1() {
            printThread("StandardNestedTest.test1()");
        }

        @Test
        void test2() {
            printThread("StandardNestedTest.test2()");
        }
    }
    
    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    class PerClassTest {

        @Test
        void test1() {
            printThread("PerClassTest.test1()");
        }

        @Test
        void test2() {
            printThread("PerClassTest.test2()");
        }
    }
    
    @Nested
    @TestMethodOrder(MethodOrderer.Alphanumeric.class)
    class OrderedTest {

        @Test
        void test1() {
            printThread("OrderedTest.test1()");
        }

        @Test
        void test2() {
            printThread("OrderedTest.test2()");
        }
    }
    
    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

Ausführungsergebnis


StandardNestedTest.test1()@ForkJoinPool-1-worker-15
StandardNestedTest.test2()@ForkJoinPool-1-worker-13
PerClassTest.test1()@ForkJoinPool-1-worker-7
OrderedTest.test1()@ForkJoinPool-1-worker-5
PerClassTest.test2()@ForkJoinPool-1-worker-7
OrderedTest.test2()@ForkJoinPool-1-worker-5

[^ 5]: Es ist eine seltsame Geschichte, Tests auszuführen, die die Reihenfolge mit @ TestMethodOrder parallel angeben.

Geben Sie die parallele Ausführung explizit an


package sample.junit5;

...
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

class JUnit5Test {

    @Nested
    class StandardNestedTest {
        ...
    }
    
    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    @Execution(ExecutionMode.CONCURRENT)
    class PerClassTest {
        ...
    }
    
    @Nested
    @TestMethodOrder(MethodOrderer.Alphanumeric.class)
    @Execution(ExecutionMode.CONCURRENT)
    class OrderedTest {
        ...
    }
    
    private static void printThread(String test) {...}
}

Ausführungsergebnis


PerClassTest.test1()@ForkJoinPool-1-worker-15
StandardNestedTest.test1()@ForkJoinPool-1-worker-13
PerClassTest.test2()@ForkJoinPool-1-worker-7
OrderedTest.test1()@ForkJoinPool-1-worker-11
OrderedTest.test2()@ForkJoinPool-1-worker-5
StandardNestedTest.test2()@ForkJoinPool-1-worker-9

Ändern Sie den Standardausführungsmodus für Klassen der obersten Ebene

junit-platform.properties


junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.classes.default=concurrent

FooTest.java


package sample.junit5;

import org.junit.jupiter.api.Test;

class FooTest {

    @Test
    void test1() {
        printThread("FooTest.test1()");
    }

    @Test
    void test2() {
        printThread("FooTest.test2()");
    }
    
    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

BarTest.java


package sample.junit5;

import org.junit.jupiter.api.Test;

class BarTest {

    @Test
    void test1() {
        printThread("BarTest.test1()");
    }

    @Test
    void test2() {
        printThread("BarTest.test2()");
    }
    
    private static void printThread(String test) {
        String name = Thread.currentThread().getName();
        System.out.printf("%s@%s%n", test, name);
    }
}

Ausführungsergebnis


BarTest.test1()@ForkJoinPool-1-worker-3
FooTest.test1()@ForkJoinPool-1-worker-7
BarTest.test2()@ForkJoinPool-1-worker-3
FooTest.test2()@ForkJoinPool-1-worker-7

junit5.jpg

Passen Sie die Anzahl der gleichzeitigen Parallelen an

Dynamische Änderung entsprechend der Anzahl der Prozessoren (Kerne)

junit-platform.properties


jjunit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=2

ParallelismCounter


package sample.junit5;

import java.util.concurrent.atomic.AtomicInteger;

public class ParallelismCounter {
    private final AtomicInteger counter = new AtomicInteger(0);
    private final AtomicInteger max = new AtomicInteger(0);
    
    public void increment() {
        this.max.set(Math.max(this.max.get(), this.counter.incrementAndGet()));
    }
    
    public void decrement() {
        this.counter.decrementAndGet();
    }
    
    public int getMaxCount() {
        return this.max.get();
    }
}

--Klasse zum Zählen der Anzahl der gleichzeitig ausgeführten Threads

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

import java.util.stream.IntStream;
import java.util.stream.Stream;

class JUnit5Test {
    private long begin;
    private ParallelismCounter counter = new ParallelismCounter();
    
    @BeforeEach
    void beforeEach() {
        begin = System.currentTimeMillis();
    }
    
    @TestFactory
    Stream<DynamicNode> testFactory() {
        return IntStream
                .range(0, 20)
                .mapToObj(i -> DynamicTest.dynamicTest("test" + i, () -> {
                    counter.increment();
                    Thread.sleep(1000);
                    counter.decrement();
                }));
    }
    
    @AfterEach
    void printThreadNames() {
        System.out.println(System.currentTimeMillis() - begin + "ms");
        System.out.println("Available Processors = " + Runtime.getRuntime().availableProcessors());
        System.out.println("Max Parallelism Count = " + counter.getMaxCount());
        System.out.println("Active Thread Count = " + Thread.activeCount());
        Thread[] activeThreads = new Thread[Thread.activeCount()];
        Thread.enumerate(activeThreads);
        IntStream.range(0, activeThreads.length)
                .mapToObj(i -> "[" + i + "] " + activeThreads[i].getName())
                .forEach(System.out::println);
    }
}

Ausführungsergebnis


2033ms
Available Processors = 8
Max Parallelism Count = 16
Active Thread Count = 18
[0] main
[1] ForkJoinPool-1-worker-19
[2] ForkJoinPool-1-worker-5
[3] ForkJoinPool-1-worker-23
[4] ForkJoinPool-1-worker-9
[5] ForkJoinPool-1-worker-27
[6] ForkJoinPool-1-worker-13
[7] ForkJoinPool-1-worker-31
[8] ForkJoinPool-1-worker-17
[9] ForkJoinPool-1-worker-3
[10] ForkJoinPool-1-worker-21
[11] ForkJoinPool-1-worker-7
[12] ForkJoinPool-1-worker-25
[13] ForkJoinPool-1-worker-11
[14] ForkJoinPool-1-worker-29
[15] ForkJoinPool-1-worker-15
[16] ForkJoinPool-1-worker-1
[17] ForkJoinPool-1-worker-33

junit-paltform.properties


junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=0.5

Ausführungsergebnis


5042ms
Available Processors = 8
Max Parallelism Count = 4
Active Thread Count = 6
[0] main
[1] ForkJoinPool-1-worker-3
[2] ForkJoinPool-1-worker-5
[3] ForkJoinPool-1-worker-7
[4] ForkJoinPool-1-worker-1
[5] ForkJoinPool-1-worker-9

Auf einen festen Wert einstellen

junit-platform.properties


junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.fixed.parallelism=6

Ausführungsergebnis


4031ms
Available Processors = 8
Max Parallelism Count = 6
Active Thread Count = 8
[0] main
[1] ForkJoinPool-1-worker-3
[2] ForkJoinPool-1-worker-5
[3] ForkJoinPool-1-worker-7
[4] ForkJoinPool-1-worker-9
[5] ForkJoinPool-1-worker-11
[6] ForkJoinPool-1-worker-13
[7] ForkJoinPool-1-worker-15

Passen Sie an, wie Sie möchten

build.gradle


...
dependencies {
    testImplementation "org.junit.jupiter:junit-jupiter:5.5.2"
    testImplementation "org.junit.jupiter:junit-jupiter-engine:5.5.2" // ★
}

--junit-jupiter-engine kann standardmäßig nur zur Laufzeit referenziert werden, daher wird es in testImplementation angegeben, damit es auch zur Kompilierungszeit referenziert werden kann.

MyParallelExecutionConfigurationStrategy


package sample.junit5;

import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfiguration;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfigurationStrategy;

public class MyParallelExecutionConfigurationStrategy implements ParallelExecutionConfigurationStrategy {
    
    @Override
    public ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {
        return new ParallelExecutionConfiguration() {

            @Override
            public int getParallelism() {
                return 7;
            }

            @Override
            public int getMinimumRunnable() {
                return 7;
            }

            @Override
            public int getMaxPoolSize() {
                return 7;
            }

            @Override
            public int getCorePoolSize() {
                return 7;
            }

            @Override
            public int getKeepAliveSeconds() {
                return 30;
            }
        };
    }
}

--Erstellen Sie eine Klasse, die "ParallelExecutionConfigurationStrategy" implementiert

junit-platform.properties


junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=custom
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.custom.class=sample.junit5.MyParallelExecutionConfigurationStrategy

Ausführungsergebnis


4034ms
Available Processors = 8
Max Parallelism Count = 7
Active Thread Count = 8
[0] main
[1] ForkJoinPool-1-worker-3
[2] ForkJoinPool-1-worker-5
[3] ForkJoinPool-1-worker-7
[4] ForkJoinPool-1-worker-9
[5] ForkJoinPool-1-worker-11
[6] ForkJoinPool-1-worker-13
[7] ForkJoinPool-1-worker-15

Ausschlusskontrolle

Wenn es keine ausschließliche Kontrolle gibt


package sample.junit5;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

@Execution(ExecutionMode.CONCURRENT)
class JUnit5Test {
    
    static int n = 0;
    
    @Test
    void test1() throws Exception {
        process("test1");
    }
    
    @Test
    void test2() throws Exception {
        process("test2");
    }

    @Test
    void test3() throws Exception {
        process("test3");
    }
    
    private void process(String name) {
        System.out.println("begin " + name);
        for (int i=0; i<10000; i++) {
            n++;
        }
        System.out.println("end " + name);
    }
    
    @AfterAll
    static void afterAll() {
        System.out.println("n = " + n);
    }
}

Ausführungsergebnis


begin test3
begin test1
begin test2
end test1
end test2
end test3
n = 13394

Wenn die ausschließliche Kontrolle erfolgt


package sample.junit5;

...
import org.junit.jupiter.api.parallel.ResourceLock;

@Execution(ExecutionMode.CONCURRENT)
class JUnit5Test {
    
    static int n = 0;
    
    @Test
    @ResourceLock("hoge")
    void test1() throws Exception { ... }
    
    @Test
    @ResourceLock("hoge")
    void test2() throws Exception { ... }

    @Test
    @ResourceLock("hoge")
    void test3() throws Exception { ... }
    
    private void process(String name) { ... }
    
    @AfterAll
    static void afterAll() { ... }
}

Ausführungsergebnis


begin test1
end test1
begin test2
end test2
begin test3
end test3
n = 30000

Geben Sie den Zugriffsmodus an

package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.api.parallel.ResourceAccessMode;
import org.junit.jupiter.api.parallel.ResourceLock;

@Execution(ExecutionMode.CONCURRENT)
class JUnit5Test {
    
    @Test   
    @ResourceLock(value = "hoge", mode = ResourceAccessMode.READ_WRITE)
    void test1() throws Exception {
        process("test1(READ_WRITE)");
    }
    
    @Test
    @ResourceLock(value = "hoge", mode = ResourceAccessMode.READ)
    void test2() throws Exception {
        process("test2(READ)");
    }

    @Test
    @ResourceLock(value = "hoge", mode = ResourceAccessMode.READ)
    void test3() throws Exception {
        process("test3(READ)");
    }
    
    private void process(String name) throws Exception {
        System.out.println("begin " + name);
        Thread.sleep(500);
        System.out.println("end " + name);
    }
}

Ausführungsergebnis


begin test1(READ_WRITE)
end test1(READ_WRITE)
begin test2(READ)
begin test3(READ)
end test2(READ)
end test3(READ)

junit5.jpg

Verwenden Sie die Standardmethode der Schnittstelle

DefaultMethodTest


package sample.junit5;

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.DynamicNode;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.List;

import static org.junit.jupiter.api.DynamicTest.*;

interface DefaultMethodTest {
    @BeforeAll
    static void beforeAll() {
        System.out.println("beforeAll()");
    }

    @BeforeEach
    default void beforeEach() {
        System.out.println("  beforeEach()");
    }
    
    @Test
    default void test() {
        System.out.println("    test()");
    }
    
    @RepeatedTest(3)
    default void repeatedTest() {
        System.out.println("    repeatedTest()");
    }
    
    @ParameterizedTest
    @ValueSource(strings = {"one", "two", "three"})
    default void parameterizedTest(String param) {
        System.out.println("    parameterizedTest(" + param + ")");
    }
    
    @TestFactory
    default List<DynamicNode> testFactory() {
        return List.of(
            dynamicTest("DynamicTest1", () -> System.out.println("    testFactory(1)")),
            dynamicTest("DynamicTest2", () -> System.out.println("    testFactory(2)"))
        );
    }
    
    @AfterEach
    default void afterEach() {
        System.out.println("  afterEach()");
    }
    
    @AfterAll
    static void afterAll() {
        System.out.println("afterAll()");
    }
}

JUnit5Test


package sample.junit5;

class JUnit5Test implements DefaultMethodTest {}

Ausführungsergebnis


beforeAll()
  beforeEach()
    repeatedTest()
  afterEach()
  beforeEach()
    repeatedTest()
  afterEach()
  beforeEach()
    repeatedTest()
  afterEach()
  beforeEach()
    testFactory(1)
    testFactory(2)
  afterEach()
  beforeEach()
    test()
  afterEach()
  beforeEach()
    parameterizedTest(one)
  afterEach()
  beforeEach()
    parameterizedTest(two)
  afterEach()
  beforeEach()
    parameterizedTest(three)
  afterEach()
afterAll()

Erweitertes Modell

JUnit Jupiter verfügt über einen Mechanismus namens ** Extension Model **, mit dem sich jede Erweiterung leicht einführen lässt.

Hello World

MyExtension


package sample.junit5;

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyExtension implements BeforeEachCallback, AfterEachCallback {
    
    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        System.out.println("MyExtension.beforeEach()");
    }

    @Override
    public void afterEach(ExtensionContext context) throws Exception {
        System.out.println("MyExtension.afterEach()");
    }
}

--Um eine Erweiterung zu erstellen, erstellen Sie zunächst eine Klasse, die die Schnittstelle Erweiterung implementiert. Machen

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MyExtension.class)
class JUnit5Test {

    @Test
    void test1() {
        System.out.println("  test1()");
    }

    @Test
    void test2() {
        System.out.println("  test2()");
    }
}

Ausführungsergebnis


MyExtension.beforeEach()
  test1()
MyExtension.afterEach()
MyExtension.beforeEach()
  test2()
MyExtension.afterEach()

test1()Wenn die Erweiterung nur auf die Methode angewendet wird


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

class JUnit5Test {

    @Test
    @ExtendWith(MyExtension.class)
    void test1() {
        System.out.println("  test1()");
    }

    @Test
    void test2() {
        System.out.println("test2()");
    }
}

Ausführungsergebnis


MyExtension.beforeEach()
  test1()
MyExtension.afterEach()
test2()

Expansionspunkt

Die folgenden Schnittstellen sind Erweiterungspunkte, die die Erweiterungsschnittstelle erben.

Schnittstelle Erläuterung
ExecutionCondition Steuert, ob der Test ausgeführt wird.
TestInstanceFactory Erstellen Sie eine Testinstanz.
TestInstancePostProcessor Führt die Initialisierungsverarbeitung nach der Generierung der Testinstanz durch.
ParameterResolver Lösen Sie Argumente wie Testmethoden und Lebenszyklusmethoden auf.
BeforeAllCallback
BeforeEachCallback
BeforeTestExecutionCallback
AfterTestExecutionCallback
AfterEachCallback
AfterAllCallback
Führen Sie die Verarbeitung entlang des Lebenszyklus aus, z. B. vor und nach der Ausführung des Tests.
TestWatcher Führen Sie die Nachbearbeitung gemäß dem Ausführungsergebnis der Testmethode durch.
TestExecutionExceptionHandler Behandelt Ausnahmen, die während der Testausführung ausgelöst werden.
LifecycleMethodExecutionExceptionHandler @BeforeEachBehandeln Sie Ausnahmen, die durch Lebenszyklusmethoden wie z.
TestTemplateInvocationContextProvider Führt eine vorbereitende Verarbeitung durch, um denselben Test in verschiedenen Kontexten auszuführen.

Unterstützungsklasse

Bei der Implementierung von Erweiterungsklassen werden Dienstprogrammklassen (Supportklassen) bereitgestellt, die für allgemeine Zwecke verwendet werden können.

Ausnahmebehandlung und problematische Beschreibung entfallen, und es ist möglich, präzise zu arbeiten.

Denken Sie vorerst daran, dass "so etwas existiert", und denken Sie beim Erstellen einer Erweiterungsklasse an "Oh, können Sie diese Unterstützungsklasse verwenden?" Und suchen Sie nach der gewünschten Methode. Ich denke es ist gut.

Diese Klassen sind keine internen Dienstprogramme, sondern werden als Hilfe bereitgestellt, wenn ein Dritter eine eigene TestEngine oder Erweiterung erstellt, sodass Sie sie sicher verwenden können.

Kontrollieren Sie die Testausführungsbedingungen

MyExecutionCondition


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyExecutionCondition implements ExecutionCondition {
    
    @Override
    public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
        if (context.getTestMethod().isPresent()) {
            System.out.println("# Test method = " + context.getRequiredTestMethod().getName());
            return context.getDisplayName().contains("o")
                    ? ConditionEvaluationResult.enabled("Test name has 'o'.")
                    : ConditionEvaluationResult.disabled("Test name does not have 'o'.");
        } else {
            System.out.println("# Test class = " + context.getRequiredTestClass().getSimpleName());
            return ConditionEvaluationResult.enabled("This is test class.");
        }
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyExecutionCondition;

import java.util.List;

@ExtendWith(MyExecutionCondition.class)
class JUnit5Test {

    @BeforeEach
    void beforeEach() {
        System.out.println("beforeEach()");
    }
    
    @Test
    void hoge() {
        System.out.println("  hoge()");
    }

    @Test
    void fuga() {
        System.out.println("  fuga()");
    }
    
    @Nested
    class NestedClass {
        
        @Test
        void piyo() {
            System.out.println("  piyo()");
        }
    }
    
    @TestFactory
    List<DynamicNode> testFactory() {
        return List.of(
            DynamicTest.dynamicTest("DynamicTest1", () -> System.out.println("  dynamicTest1")),
            DynamicTest.dynamicTest("DynamicTest2", () -> System.out.println("  dynamicTest2"))
        );
    }
}

Ausführungsergebnis


# Test class = JUnit5Test
# Test method = testFactory
beforeEach()
  dynamicTest1
  dynamicTest2
# Test method = fuga
# Test method = hoge
beforeEach()
  hoge()
# Test class = NestedClass
# Test method = piyo
beforeEach()
  piyo()

[^ 6]: In der Dokumentation steht "Container", aber nicht genau, worauf sich "Container" bezieht (wahrscheinlich eine Testklasse oder ein dynamischer Test wie "DynamicContainer"). Ich denke, es bezieht sich auf die Katamari, die die Testmethoden zusammenfasst.

Erstellen Sie eine Testinstanz

MyTestInstanceFactory


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstanceFactory;
import org.junit.jupiter.api.extension.TestInstanceFactoryContext;
import org.junit.jupiter.api.extension.TestInstantiationException;
import org.junit.platform.commons.support.ReflectionSupport;

public class MyTestInstanceFactory implements TestInstanceFactory {
    @Override
    public Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) throws TestInstantiationException {
        Class<?> testClass = factoryContext.getTestClass();
        System.out.println("===========================================");
        System.out.println("* testClass=" + testClass);
        System.out.println("* outerInstance=" + factoryContext.getOuterInstance().orElse("<empty>"));

        
        return factoryContext
                .getOuterInstance()
                .map(outerInstance -> {
                    Object instance = ReflectionSupport.newInstance(testClass, outerInstance);
                    System.out.println("* outerInstance [" + outerInstance.hashCode() + "]");
                    System.out.println("* testInstance [" + instance.hashCode() + "]");
                    return instance;
                })
                .orElseGet(() -> {
                    Object instance = ReflectionSupport.newInstance(testClass);
                    System.out.println("* testInstance [" + instance.hashCode() + "]");
                    return instance;
                });
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyTestInstanceFactory;

import java.util.List;

@ExtendWith(MyTestInstanceFactory.class)
class JUnit5Test {
    
    @Test
    void test() {
        System.out.println("JUnit5Test.test() [" + this.hashCode() + "]");
    }
    
    @Nested
    class NestedClass {
        
        @Test
        void test() {
            System.out.println("NestedClass.test() [" + this.hashCode() + "]");
        }
    }
    
    @TestFactory
    List<DynamicNode> testFactory() {
        return List.of(
            DynamicTest.dynamicTest("DynamicTest1", () -> System.out.println("JUnit5Test.dynamicTest1() [" + this.hashCode() + "]")),
            DynamicTest.dynamicTest("DynamicTest2", () -> System.out.println("JUnit5Test.dynamicTest2() [" + this.hashCode() + "]"))
        );
    }
}

Ausführungsergebnis


===========================================
* testClass=class sample.junit5.JUnit5Test
* outerInstance=<empty>
* testInstance [1781155104]
JUnit5Test.dynamicTest1() [1781155104]
JUnit5Test.dynamicTest2() [1781155104]
===========================================
* testClass=class sample.junit5.JUnit5Test
* outerInstance=<empty>
* testInstance [667449440]
JUnit5Test.test() [667449440]
===========================================
* testClass=class sample.junit5.JUnit5Test
* outerInstance=<empty>
* testInstance [1846668301]
===========================================
* testClass=class sample.junit5.JUnit5Test$NestedClass
* outerInstance=sample.junit5.JUnit5Test@6e11ec0d
* outerInstance [1846668301]
* testInstance [1282836833]
NestedClass.test() [1282836833]

Führen Sie die Initialisierungsverarbeitung nach der Generierung der Testinstanz durch

MyTestInstancePostProcessor


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;

public class MyTestInstancePostProcessor implements TestInstancePostProcessor {
    
    @Override
    public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
        System.out.println("testInstance.hash = " + testInstance.hashCode());
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyTestInstancePostProcessor;

@ExtendWith(MyTestInstancePostProcessor.class)
class JUnit5Test {
    
    @Test
    void test1() {
        System.out.println("test1() [" + this.hashCode() + "]");
    }

    @Test
    void test2() {
        System.out.println("test2() [" + this.hashCode() + "]");
    }
}

Ausführungsergebnis


testInstance.hash = 756008141
test1() [756008141]
testInstance.hash = 282044315
test2() [282044315]

Parameter auflösen

MyParameterResolver


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;

import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.util.Optional;

public class MyParameterResolver implements ParameterResolver {
    
    @Override
    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
        Executable executable = parameterContext.getDeclaringExecutable();
        int index = parameterContext.getIndex();
        Parameter parameter = parameterContext.getParameter();
        Optional<Object> target = parameterContext.getTarget();

        System.out.printf(
            "target=%s, executable=%s, index=%d, parameter=%s%n",
            target.orElse("<empty>"),
            executable.getName(),
            index,
            parameter.getName()
        );
        
        return true;
    }

    @Override
    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
        Class<?> type = parameterContext.getParameter().getType();
        if (type.equals(String.class)) {
            return "Hello";
        } else if (type.equals(int.class)) {
            return 999;
        } else {
            return 12.34;
        }
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyParameterResolver;

import java.util.List;

@ExtendWith(MyParameterResolver.class)
class JUnit5Test {

    @BeforeEach
    void beforeEach(int i) {
        System.out.printf("beforeEach(i=%d)%n", i);
    }

    @TestFactory
    List<DynamicNode> dynamicTest(String string) {
        return List.of(DynamicTest.dynamicTest("DynamicTest", () -> System.out.printf("dynamicTest(string=%s)%n", string)));
    }
    
    @Test
    void test1(String string, double d, int i) {
        System.out.printf("test1(string=%s, d=%f, i=%d)%n", string, d, i);
    }
    
    @Nested
    class NestedClass {
        
        @Test
        void test2(double d) {
            System.out.printf("test2(d=%f)%n", d);
        }
    }
    
    @AfterEach
    void afterEach() {
        System.out.println("-----------------------------------------");
    }
}

Ausführungsergebnis


target=sample.junit5.JUnit5Test@5e101011, executable=beforeEach, index=0, parameter=i
beforeEach(i=999)
target=sample.junit5.JUnit5Test@5e101011, executable=dynamicTest, index=0, parameter=string
dynamicTest(string=Hello)
-----------------------------------------
target=sample.junit5.JUnit5Test@4d3b0c46, executable=beforeEach, index=0, parameter=i
beforeEach(i=999)
target=sample.junit5.JUnit5Test@4d3b0c46, executable=test1, index=0, parameter=string
target=sample.junit5.JUnit5Test@4d3b0c46, executable=test1, index=1, parameter=d
target=sample.junit5.JUnit5Test@4d3b0c46, executable=test1, index=2, parameter=i
test1(string=Hello, d=12.340000, i=999)
-----------------------------------------
target=sample.junit5.JUnit5Test@7d9ea927, executable=beforeEach, index=0, parameter=i
beforeEach(i=999)
target=sample.junit5.JUnit5Test$NestedClass@710edef, executable=test2, index=0, parameter=d
test2(d=12.340000)
-----------------------------------------

Führen Sie die Verarbeitung entlang des Lebenszyklus durch

MyLifeCycleCallback


package sample.junit5.extension;

import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyLifeCycleCallback
    implements BeforeAllCallback,
               BeforeEachCallback,
               BeforeTestExecutionCallback,
               AfterTestExecutionCallback,
               AfterEachCallback,
               AfterAllCallback {
    
    @Override
    public void beforeAll(ExtensionContext context) throws Exception {
        System.out.println("BeforeAllCallback");
    }

    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        System.out.println("    BeforeEachCallback");
    }

    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        System.out.println("        BeforeTestExecutionCallback");
    }

    @Override
    public void afterTestExecution(ExtensionContext context) throws Exception {
        System.out.println("        AfterTestExecutionCallback");
    }

    @Override
    public void afterEach(ExtensionContext context) throws Exception {
        System.out.println("    AfterEachCallback");
    }

    @Override
    public void afterAll(ExtensionContext context) throws Exception {
        System.out.println("AfterAllCallback");
    }
}

JUnit5Test


package sample.junit5;

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.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyLifeCycleCallback;

@ExtendWith(MyLifeCycleCallback.class)
class JUnit5Test {
    @BeforeAll
    static void beforeAll() {
        System.out.println("  beforeAll()");
    }
    
    @BeforeEach
    void beforeEach() {
        System.out.println("      beforeEach()");
    }
    
    @Test
    void test() {
        System.out.println("          test()");
    }
    
    @AfterEach
    void afterEach() {
        System.out.println("      afterEach()");
    }
    
    @AfterAll
    static void afterAll() {
        System.out.println("  afterAll()");
    }
}

Ausführungsergebnis


BeforeAllCallback
  beforeAll()
    BeforeEachCallback
      beforeEach()
        BeforeTestExecutionCallback
          test()
        AfterTestExecutionCallback
      afterEach()
    AfterEachCallback
  afterAll()
AfterAllCallback

Führen Sie die Verarbeitung gemäß dem Testergebnis durch

MyTestWatcher


package sample.junit5.extension;

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestWatcher;

import java.util.Optional;

public class MyTestWatcher implements TestWatcher, AfterEachCallback {

    @Override
    public void testDisabled(ExtensionContext context, Optional<String> reason) {
        System.out.println("disabled : test=" + context.getDisplayName() + ", reason=" + reason.orElse("<empty>"));
    }

    @Override
    public void testSuccessful(ExtensionContext context) {
        System.out.println("successful : test=" + context.getDisplayName());
    }

    @Override
    public void testAborted(ExtensionContext context, Throwable cause) {
        System.out.println("aborted : test=" + context.getDisplayName() + ", cause=" + cause);
    }

    @Override
    public void testFailed(ExtensionContext context, Throwable cause) {
        System.out.println("failed : test=" + context.getDisplayName() + ", cause=" + cause);
    }

    @Override
    public void afterEach(ExtensionContext context) throws Exception {
        System.out.println("AfterEachCallback");
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyTestWatcher;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*;

@ExtendWith(MyTestWatcher.class)
class JUnit5Test {
    
    @Test
    void testSuccessful() {
        System.out.println("testSuccessful()");
        assertEquals(10, 10);
    }
    
    @Test
    void testFailed() {
        System.out.println("testFailed()");
        assertEquals(10, 20);
    }
    
    @Test
    @Disabled("REASON")
    void testDisabled() {
        System.out.println("testDisabled()");
    }
    
    @Test
    void testAborted() {
        System.out.println("testAborted()");
        assumeTrue(false, "test abort");
    }
}

Ausführungsergebnis


testAborted()
AfterEachCallback
aborted : test=testAborted(), cause=org.opentest4j.TestAbortedException: Assumption failed: test abort

testSuccessful()
AfterEachCallback
successful : test=testSuccessful()

disabled : test=testDisabled(), reason=REASON

testFailed()
AfterEachCallback
failed : test=testFailed(), cause=org.opentest4j.AssertionFailedError: expected: <10> but was: <20>

Behandeln Sie im Test ausgelöste Ausnahmen

MyTestExecutionExceptionHandler


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;

public class MyTestExecutionExceptionHandler implements TestExecutionExceptionHandler {
    
    @Override
    public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
        System.out.println(" * throwable=" + throwable);
        if (throwable instanceof NullPointerException) {
            throw throwable;
        } else if (throwable instanceof IllegalStateException) {
            throw new UnsupportedOperationException("test");
        }
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyTestExecutionExceptionHandler;

import java.io.IOException;

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

@ExtendWith(MyTestExecutionExceptionHandler.class)
class JUnit5Test {
    
    @Test
    void success() {
        System.out.println("success()");
        assertEquals(10, 10);
    }
    
    @Test
    void fail() {
        System.out.println("fail()");
        assertEquals(10, 20);
    }
    
    @Test
    void throwsIOException() throws Exception {
        System.out.println("throwsIOException()");
        throw new IOException("test");
    }
    
    @Test
    void throwsNullPointerException() {
        System.out.println("throwsNullPointerException()");
        throw new NullPointerException("test");
    }

    @Test
    void throwsIllegalStateException() {
        System.out.println("throwsIllegalStateException()");
        throw new IllegalStateException("test");
    }
}

Ausführungsergebnis


success()
fail()
 * throwable=org.opentest4j.AssertionFailedError: expected: <10> but was: <20>
throwsNullPointerException()
 * throwable=java.lang.NullPointerException: test
throwsIllegalStateException()
 * throwable=java.lang.IllegalStateException: test
throwsIOException()
 * throwable=java.io.IOException: test
.
'-- JUnit Jupiter [OK]
  +-- JUnit5Test [OK]
  | +-- success() [OK]
  | +-- fail() [OK]
  | +-- throwsNullPointerException() [X] test
  | +-- throwsIllegalStateException() [X] test
  | '-- throwsIOException() [OK]
  '-- ParallelismCheck [S] class sample.junit5.ParallelismCheck is @Disabled

Failures (2):
  JUnit Jupiter:JUnit5Test:throwsNullPointerException()
    MethodSource [className = 'sample.junit5.JUnit5Test', methodName = 'throwsNullPointerException', methodParameterTypes = '']
    => java.lang.NullPointerException: test
       ...

  JUnit Jupiter:JUnit5Test:throwsIllegalStateException()
    MethodSource [className = 'sample.junit5.JUnit5Test', methodName = 'throwsIllegalStateException', methodParameterTypes = '']
    => java.lang.UnsupportedOperationException: test

Behandeln Sie Ausnahmen, die von Lebenszyklusmethoden ausgelöst werden

MyTestExecutionExceptionHandler


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;

public class MyTestExecutionExceptionHandler implements TestExecutionExceptionHandler {
    
    @Override
    public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
        System.out.println("[TestExecutionExceptionHandler] throwable=" + throwable);
    }
}

MyLifecycleMethodExecutionExceptionHandler


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;

public class MyLifecycleMethodExecutionExceptionHandler implements LifecycleMethodExecutionExceptionHandler {

    @Override
    public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
        System.out.println("[LifecycleMethodExecutionExceptionHandler] throwable=" + throwable);
        throw throwable;
    }

    @Override
    public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
        System.out.println("[LifecycleMethodExecutionExceptionHandler] throwable=" + throwable);
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyLifecycleMethodExecutionExceptionHandler;
import sample.junit5.extension.MyTestExecutionExceptionHandler;

class JUnit5Test {
    
    @Nested
    class InnerClass1 {
        
        @Test
        @DisplayName("Wenn die Lebenszyklusmethode normalerweise eine Ausnahme auslöst")
        void test() {
            System.out.println("InnerClass1");
        }
    }

    @Nested
    @ExtendWith(MyTestExecutionExceptionHandler.class)
    class InnerClass2 {

        @Test
        @DisplayName("Funktionsweise von TestExecutionExceptionHandler für Ausnahmen, die in Lebenszyklusmethoden ausgelöst werden")
        void test() {
            System.out.println("InnerClass2");
        }
    }

    @Nested
    @ExtendWith(MyLifecycleMethodExecutionExceptionHandler.class)
    class InnerClass3 {

        @Test
        @DisplayName("Wenn die von der Lifecycle-Methode ausgelöste Ausnahme von LifecycleMethodExecutionExceptionHandler gequetscht wird")
        void test() {
            System.out.println("InnerClass3");
        }
    }

    @Nested
    @ExtendWith(MyLifecycleMethodExecutionExceptionHandler.class)
    class InnerClass4 {

        @BeforeEach
        void beforeEach(TestInfo testInfo) {
            throw new RuntimeException("beforeEach@" + simpleClassName(testInfo));
        }
        
        @Test
        @DisplayName("Wenn der LifecycleMethodExecutionExceptionHandler die von der Lifecycle-Methode ausgelöste Ausnahme nicht unterdrückt")
        void test() {
            System.out.println("InnerClass4");
        }
    }
    
    @AfterEach
    void afterEach(TestInfo testInfo) {
        throw new RuntimeException("afterEach@" + simpleClassName(testInfo));
    }
    
    static String simpleClassName(TestInfo testInfo) {
        return testInfo.getTestClass().map(Class::getSimpleName).orElse("<empty>");
    }
}

Ausführungsergebnis


[LifecycleMethodExecutionExceptionHandler] throwable=java.lang.RuntimeException: beforeEach@InnerClass4
[LifecycleMethodExecutionExceptionHandler] throwable=java.lang.RuntimeException: afterEach@InnerClass4
InnerClass3
[LifecycleMethodExecutionExceptionHandler] throwable=java.lang.RuntimeException: afterEach@InnerClass3
InnerClass2
InnerClass1

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    +-- InnerClass4 [OK]
    | '--Wenn der LifecycleMethodExecutionExceptionHandler die von der Lifecycle-Methode ausgelöste Ausnahme nicht unterdrückt[X] beforeEach@InnerClass4
    +-- InnerClass3 [OK]
    | '--Wenn die von der Lifecycle-Methode ausgelöste Ausnahme von LifecycleMethodExecutionExceptionHandler gequetscht wird[OK]
    +-- InnerClass2 [OK]
    | '--Funktionsweise von TestExecutionExceptionHandler für Ausnahmen, die in Lebenszyklusmethoden ausgelöst werden[X] afterEach@InnerClass2
    '-- InnerClass1 [OK]
      '--Wenn die Lebenszyklusmethode normalerweise eine Ausnahme auslöst[X] afterEach@InnerClass1

Führen Sie denselben Test in verschiedenen Kontexten aus

MyTestTemplateInvocationContextProvider


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;

import java.util.stream.Stream;

public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        System.out.println("[supportsTestTemplate] displayName=" + context.getDisplayName());
        return context.getDisplayName().equals("test1()");
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        System.out.println("[provideTestTemplateInvocationContexts] displayName=" + context.getDisplayName());
        return Stream.of(
            new MyTestTemplateInvocationContext(),
            new MyTestTemplateInvocationContext(),
            new MyTestTemplateInvocationContext()
        );
    }
    
    public static class MyTestTemplateInvocationContext implements TestTemplateInvocationContext {
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyTestTemplateInvocationContextProvider;

@ExtendWith(MyTestTemplateInvocationContextProvider.class)
class JUnit5Test {

    @TestTemplate
    void test1() {
        System.out.println("test1()");
    }

    @TestTemplate
    void test2() {
        System.out.println("test2()");
    }

    @Test
    void test3() {
        System.out.println("test3()");
    }
}

Ausführungsergebnis


[supportsTestTemplate] displayName=test1()
[provideTestTemplateInvocationContexts] displayName=test1()
test1()
test1()
test1()
[supportsTestTemplate] displayName=test2()
test3()

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    +-- test1() [OK]
    | +-- [1] [OK]
    | +-- [2] [OK]
    | '-- [3] [OK]
    +-- test2() [X] You must register at least one TestTemplateInvocationContextProvider that supports @TestTemplate method [void sample.junit5.JUnit5Test.test2()]
    '-- test3() [OK]

Failures (1):
  JUnit Jupiter:JUnit5Test:test2()
    MethodSource [className = 'sample.junit5.JUnit5Test', methodName = 'test2', methodParameterTypes = '']
    => org.junit.platform.commons.PreconditionViolationException: You must register at least one TestTemplateInvocationContextProvider that supports @TestTemplate method [void sample.junit5.JUnit5Test.test2()]
       ...

Geben Sie den Anzeigenamen im Kontext an

MyTestTemplateInvocationContextProvider


package sample.junit5.extension;

...

public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
    ...

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        System.out.println("[provideTestTemplateInvocationContexts] displayName=" + context.getDisplayName());
        return Stream.of(
            new MyTestTemplateInvocationContext("Hoge"),
            new MyTestTemplateInvocationContext("Fuga"),
            new MyTestTemplateInvocationContext("Piyo")
        );
    }
    
    public static class MyTestTemplateInvocationContext implements TestTemplateInvocationContext {
        private final String name;

        public MyTestTemplateInvocationContext(String name) {
            this.name = name;
        }

        @Override
        public String getDisplayName(int invocationIndex) {
            return this.name + "[" + invocationIndex + "]";
        }
    }
}

Ausführungsergebnis


.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    +-- test1() [OK]
    | +-- Hoge[1] [OK]
    | +-- Fuga[2] [OK]
    | '-- Piyo[3] [OK]
    :

Fügen Sie für jeden Kontext beliebige Erweiterungen hinzu

MyTestTemplateInvocationContextProvider


package sample.junit5.extension;

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;

import java.util.List;
import java.util.stream.Stream;

public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
    ...

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        System.out.println("[provideTestTemplateInvocationContexts] displayName=" + context.getDisplayName());
        return Stream.of(
            new MyTestTemplateInvocationContext("BeforeEach", (BeforeEachCallback) ctx -> {
                System.out.println("beforeEachCallback()");
            }),
            new MyTestTemplateInvocationContext("AfterEach", (AfterEachCallback) ctx -> {
                System.out.println("afterEachCallback()");
            })
        );
    }
    
    public static class MyTestTemplateInvocationContext implements TestTemplateInvocationContext {
        private final String name;
        private final Extension extension;

        public MyTestTemplateInvocationContext(String name, Extension extension) {
            this.name = name;
            this.extension = extension;
        }

        @Override
        public String getDisplayName(int invocationIndex) {
            return this.name;
        }

        @Override
        public List<Extension> getAdditionalExtensions() {
            return List.of(this.extension);
        }
    }
}

Ausführungsergebnis


...
beforeEachCallback()
test1()
test1()
afterEachCallback()
...

.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    +-- test1() [OK]
    | +-- BeforeEach [OK]
    | '-- AfterEach [OK]
    :

Registrieren Sie die Verlängerung prozedural

MyRegisterExtension


package sample.junit5.extension;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyRegisterExtension implements BeforeEachCallback, BeforeAllCallback {
    private final String name;

    public MyRegisterExtension(String name) {
        this.name = name;
    }

    @Override
    public void beforeAll(ExtensionContext context) throws Exception {
        System.out.println("[" + this.name + "] beforeAll()");
    }

    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        System.out.println("[" + this.name + "] beforeEach()");
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import sample.junit5.extension.MyRegisterExtension;

class JUnit5Test {
    @RegisterExtension
    static MyRegisterExtension classField = new MyRegisterExtension("classField");
    @RegisterExtension
    MyRegisterExtension instanceField = new MyRegisterExtension("instanceField");

    @BeforeAll
    static void beforeAll() {
        System.out.println("beforeAll()");
    }
    
    @BeforeEach
    void beforeEach() {
        System.out.println("beforeEach()");
    }
    
    @Test
    void test1() {
        System.out.println("test1()");
    }
}

Ausführungsergebnis


[classField] beforeAll()
beforeAll()
[classField] beforeEach()
[instanceField] beforeEach()
beforeEach()
test1()

[^ 7]: Sie können es einfach mit einem Konstruktor generieren, einen Mechanismus wie einen Builder vorbereiten oder eine Factory-Methode erstellen.

Registrieren Sie sich automatisch mit ServiceLoader

Ordnerstruktur


`-src/test/
  |-java/
  | `-sample/junit5/
  |   `-JUnit5Test.java
  `-resources/
    |-junit-platform.properties
    `-META-INF/services/
      `-org.junit.jupiter.api.extension.Extension

text:org.junit.jupiter.api.extension.Extension


sample.junit5.extension.MyServiceLoaderExtension

junit-platform.properties


junit.jupiter.extensions.autodetection.enabled=true

MyServiceLoaderExtension


package sample.junit5.extension;

import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyServiceLoaderExtension implements BeforeEachCallback {
    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        System.out.println("MyServiceLoaderExtension.beforeEach()");
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;

class JUnit5Test {
    
    @Test
    void test1() {
        System.out.println("test1()");
    }
}

Ausführungsergebnis


MyServiceLoaderExtension.beforeEach()
test1()

Überlegen Sie, wie Sie auf die Informationen verweisen können, die aufgezeichnet werden, wenn eine Erweiterung ausgeführt wird, wenn eine andere Erweiterung ausgeführt wird.

Beispiel: Ein Bild, das die Startzeit einer Testmethode mit "BeforeEachCallback" aufzeichnet und die Ausführungszeit aus der Differenz zwischen der aktuellen Zeit und der Startzeit mit "AfterEachCallback" ausgibt.

Wenn Sie eine Erweiterung in einer einzelnen Klasse erstellen, fällt Ihnen eine schnelle Möglichkeit zur Verwendung von Instanzvariablen ein.

MyStopwatch


package sample.junit5.extension;

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyStopwatch implements BeforeEachCallback, AfterEachCallback {
    
    private long startTime;

    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        this.startTime = System.currentTimeMillis();
    }

    @Override
    public void afterEach(ExtensionContext context) throws Exception {
        String displayName = context.getDisplayName();
        long endTime = System.currentTimeMillis();
        long time = endTime - this.startTime;
        System.out.println("[" + displayName + "] time=" + time + " (startTime=" + this.startTime + ", endTime=" + endTime + ")");
    }
}

Wenden Sie es tatsächlich auf den folgenden Test an und führen Sie es aus.

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyStopwatch;

import java.util.concurrent.TimeUnit;

@ExtendWith(MyStopwatch.class)
class JUnit5Test {

    @Test
    void test1() throws Exception {
        TimeUnit.MILLISECONDS.sleep(200);
    }
    
    @Test
    void test2() throws Exception {
        TimeUnit.MILLISECONDS.sleep(400);
    }

    @Test
    void test3() throws Exception {
        TimeUnit.MILLISECONDS.sleep(600);
    }
}

Ausführungsergebnis


[test1()] time=211 (startTime=1577191073870, endTime=1577191074081)
[test2()] time=400 (startTime=1577191074126, endTime=1577191074526)
[test3()] time=602 (startTime=1577191074529, endTime=1577191075131)

Es hat so funktioniert.

Es gibt jedoch ** Probleme mit dieser Implementierung **.

Wenn dieser Test parallel ausgeführt wird, wird das Problem offensichtlich.

junit-platform.properties


junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=2
junit.jupiter.execution.parallel.mode.default=concurrent

Ausführungsergebnis


[test1()] time=214 (startTime=1577191442987, endTime=1577191443201)
[test3()] time=363 (startTime=1577191443234, endTime=1577191443597)
[test2()] time=402 (startTime=1577191443234, endTime=1577191443636)

Die Ausführungszeit von "test3 ()" beträgt ungefähr 300 ms (tatsächlich ist dieser Wert unmöglich, da er 600 ms lang schläft).

Wenn Sie genau hinschauen, können Sie sehen, dass startTime von test3 () undtest2 ()den gleichen Wert haben. Dies bedeutet, dass die zur Zeitmessung von test3 () verwendete Startzeit zur Startzeit von test2 () geworden ist.

Die Ursache für dieses Problem ist, dass die Startzeit von der Instanzvariablen MyStopwatch gemeinsam genutzt wird. MyStopwatch verwendet dieselbe Instanz, während der JUnit5Test -Test ausgeführt wird. Mit anderen Worten, die "MyStopwatch", die bei der Ausführung jeder Testmethode verwendet wird, ist dieselbe Instanz.

Wenn "test3 ()" ausgeführt wird, zeichnet "beforeEach ()" die Startzeit in "startTime" auf. Unmittelbar danach wurde jedoch "test2 ()" parallel ausgeführt, und der Wert von "startTime" wurde durch die Startzeit von "test2 ()" überschrieben. Infolgedessen sind die oben genannten Probleme aufgetreten.

Auf diese Weise kann die Verwendung von Instanzvariablen der Implementierungsklasse zum Teilen von Daten zwischen Erweiterungen zu unerwarteten Problemen führen, abhängig davon, wie die Tests ausgeführt werden. (Es mag andere Muster geben, die Probleme verursachen, aber vorerst kann ich mir nur diesen Fall der parallelen Ausführung vorstellen.)

Verwenden Sie Store

Ich weiß nicht, ob es dieses Problem lösen soll, aber mit "Store" können Sie die gemeinsame Nutzung von Daten implementieren, damit bei paralleler Ausführung keine Probleme auftreten.

MyStopwatch


package sample.junit5.extension;

import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyStopwatch implements BeforeEachCallback, AfterEachCallback {
    
    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create("stopwatch"));
        store.put("startTime", System.currentTimeMillis());
    }

    @Override
    public void afterEach(ExtensionContext context) throws Exception {
        ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create("stopwatch"));
        long startTime = store.get("startTime", long.class);

        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;

        String displayName = context.getDisplayName();
        System.out.println("[" + displayName + "] time=" + time + " (startTime=" + startTime + ", endTime=" + endTime + ")");
    }
}

Ausführungsergebnis


[test1()] time=213 (startTime=1577193397891, endTime=1577193398104)
[test3()] time=609 (startTime=1577193397891, endTime=1577193398500)
[test2()] time=401 (startTime=1577193398142, endTime=1577193398543)

Die Zeit von "test3 ()" beträgt ungefähr 600 ms und es funktioniert gut. (StartTime ist dasselbe wie test1 () weiltest1 ()und test3 () gleichzeitig mit 2 Parallelen gestartet werden, also gibt es kein Problem)

Diese Implementierung verwendet einen Mechanismus namens Store. Store ist ein Datencontainer, der für jeden ExtensionContext vorbereitet wurde, und alle Daten können im Schlüsselwertformat gespeichert werden.

ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create("stopwatch"));
store.put("startTime", System.currentTimeMillis());

...

long startTime = store.get("startTime", long.class);

Eine Instanz von "Store" ist "getStore (Namespace)" von ExtensionContext (https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/ExtensionContext.html#getStore(org). Es kann mit der Methode .junit.jupiter.api.extension.ExtensionContext.Namespace)) abgerufen werden. Geben Sie als Argument Namespace an.

Als Bild werden mehrere Stores in ExtensionContext gespeichert, und es scheint, als würden Sie angeben, welchen Store Sie mit Namespace erhalten möchten. (Eigentlich wird "Namespace" nur als Teil des Schlüssels verwendet, und der eigentliche "Store" wird von einer einzelnen "Map" implementiert, aber ich denke, das ist als Bild in Ordnung.)

junit5.jpg

Durch Trennen von "Store" in "Namespace" auf diese Weise kann "Store" getrennt und Daten gemeinsam genutzt werden, selbst wenn mehrere Erweiterungen denselben Schlüssel verwenden. Wenn Sie Daten für alle Erweiterungen freigeben möchten, Namespace.GLOBAL Sie können auch eine vordefinierte Konstante mit dem Namen .html # GLOBAL verwenden.

[Create (Object ...)](https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/ExtensionContext.Namespace.html#create zum Erstellen von Namespace (java.lang.Object ...)) Verwenden Sie die Methode. Jedes Objekt kann als Argument angegeben werden, es muss jedoch ein Objekt sein, das mit der Methode equals () verglichen und verifiziert werden kann.

So finden Sie den Lebenszyklus und die Schlüssel des Geschäfts

Der Lebenszyklus des Stores entspricht dem "ExtensionContext", von dem er bezogen wurde.

MyExtension


package sample.junit5.extension;

import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, AfterAllCallback {

    @Override
    public void beforeAll(ExtensionContext context) throws Exception {
        System.out.println("[beforeAll]");
        this.printStoreValues(context);
        
        ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create("foo"));
        store.put("hoge", "INITIAL VALUE");
    }
    
    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        System.out.println("[beforeEach@" + context.getDisplayName() + "]");
        this.printStoreValues(context);

        ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create("foo"));
        store.put("hoge", context.getDisplayName());
    }

    @Override
    public void afterEach(ExtensionContext context) throws Exception {
        System.out.println("[afterEach@" + context.getDisplayName() + "]");
        this.printStoreValues(context);
    }

    @Override
    public void afterAll(ExtensionContext context) throws Exception {
        System.out.println("[afterAll]");
        this.printStoreValues(context);
    }
    
    private void printStoreValues(ExtensionContext context) {
        ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create("foo"));
        System.out.println("  hoge=" + store.get("hoge"));
        System.out.println("  context.class=" + context.getClass().getCanonicalName());
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyExtension;

@ExtendWith(MyExtension.class)
class JUnit5Test {

    @Test
    void test1() throws Exception {}
    
    @Test
    void test2() throws Exception {}
}

Ausführungsergebnis


[beforeAll]
  hoge=null
  context.class=org.junit.jupiter.engine.descriptor.ClassExtensionContext

[beforeEach@test1()]
  hoge=INITIAL VALUE
  context.class=org.junit.jupiter.engine.descriptor.MethodExtensionContext
[afterEach@test1()]
  hoge=test1()
  context.class=org.junit.jupiter.engine.descriptor.MethodExtensionContext

[beforeEach@test2()]
  hoge=INITIAL VALUE
  context.class=org.junit.jupiter.engine.descriptor.MethodExtensionContext
[afterEach@test2()]
  hoge=test2()
  context.class=org.junit.jupiter.engine.descriptor.MethodExtensionContext

[afterAll]
  hoge=INITIAL VALUE
  context.class=org.junit.jupiter.engine.descriptor.ClassExtensionContext

junit5.jpg

Führen Sie die Verarbeitung am Ende des Lebenszyklus durch

MyCloseableResource


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;

public class MyCloseableResource implements ExtensionContext.Store.CloseableResource {
    private final String name;

    public MyCloseableResource(String name) {
        this.name = name;
    }

    @Override
    public void close() throws Throwable {
        System.out.println("  Close Resource > " + this.name);
    }
}

MyExtension


package sample.junit5.extension;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyExtension implements BeforeEachCallback, BeforeAllCallback {

    @Override
    public void beforeAll(ExtensionContext context) throws Exception {
        MyCloseableResource resource = new MyCloseableResource("BeforeAll");
        context.getStore(ExtensionContext.Namespace.GLOBAL).put("foo", resource);
    }

    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        MyCloseableResource resource = new MyCloseableResource("BeforeEach(" + context.getDisplayName() + ")");
        context.getStore(ExtensionContext.Namespace.GLOBAL).put("foo", resource);
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyExtension;

@ExtendWith(MyExtension.class)
class JUnit5Test {

    @Test
    void test1() throws Exception {
        System.out.println("test1()");
    }
    
    @Test
    void test2() throws Exception {
        System.out.println("test2()");
    }
    
    @AfterAll
    static void afterAll() {
        System.out.println("afterAll()");
    }
}

Ausführungsergebnis


test1()
  Close Resource > BeforeEach(test1())
test2()
  Close Resource > BeforeEach(test2())
afterAll()
  Close Resource > BeforeAll

Meta-Annotation

MyParameterResolver


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;

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

    @Override
    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
        return extensionContext.getRequiredTestMethod().getName();
    }
}

MyParameterResolverExtension


package sample.junit5.extension;

import org.junit.jupiter.api.extension.ExtendWith;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ExtendWith(MyParameterResolver.class)
public @interface MyParameterResolverExtension {}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import sample.junit5.extension.MyParameterResolverExtension;

@MyParameterResolverExtension
class JUnit5Test {
    
    @Test
    void test1(String testMethodName) {
        System.out.println("[test1] testMethodName=" + testMethodName);
    }

    @Test
    void test2(String testMethodName) {
        System.out.println("[test2] testMethodName=" + testMethodName);
    }
}

Ausführungsergebnis


[test1] testMethodName=test1
[test2] testMethodName=test2

--JUnit Jupiter unterstützt den Meta-Annotation-Mechanismus

Parameter einstellen

Anpassen des Testverhaltens wie "junit.jupiter.testinstance.lifecycle.default" zum Ändern des Standardlebenszyklus einer Testinstanz und "junit.jupiter.execution.parallel.enabled" zum Aktivieren der parallelen Ausführung. Die Parameter heißen ** Konfigurationsparameter **.

Die folgenden drei Methoden stehen zur Angabe der Einstellparameter zur Verfügung.

  1. Geben Sie die für jeden Launcher vorbereitete Methode an
  2. In den JVM-Systemeigenschaften angegeben
  3. Geben Sie in "junit-platform.properties" an

Die erste Methode, die für jeden Launcher bereitgestellt wird, besteht beispielsweise darin, bei Verwendung von ConsoleLauncher diese mit der Option "--config" anzugeben. Gradle scheint diese Spezifikationsmethode derzeit nicht zu unterstützen (stattdessen werden Systemeigenschaften oder "junit-platform.properties" verwendet). Geben Sie für Maven mit der Eigenschaft configurationParameters an.

Die zweite JVM-Systemeigenschaft wird wie in der Systemeigenschaft angegeben (die Spezifikationsmethode unterscheidet sich je nach Launcher).

Die dritte Option wird aktiviert, indem eine Eigenschaftendatei mit dem Namen "junit-platform.properties" im Stammverzeichnis des Klassenpfads abgelegt wird.

Wenn dieselben Schlüsselkonfigurationsparameter auf diese unterschiedliche Weise angegeben werden, hat der oben definierte Wert Vorrang. Das heißt, der für jeden Launcher bereitgestellte Wert hat die höchste Priorität, und der in "junit-platform.properties" definierte Wert hat die niedrigste Priorität.

Versuchen Sie es tatsächlich mit ConsoleLauncher.

MyExtension


package sample.junit5.extension;

import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class MyExtension implements BeforeEachCallback {
    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        this.printConfigurationParameter(context, "hoge");
        this.printConfigurationParameter(context, "fuga");
        this.printConfigurationParameter(context, "piyo");
    }
    
    private void printConfigurationParameter(ExtensionContext context, String key) {
        System.out.println("key=" + key + ", value=" + context.getConfigurationParameter(key).orElse("<empty>"));
    }
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.junit5.extension.MyExtension;

@ExtendWith(MyExtension.class)
class JUnit5Test {
    
    @Test
    void test1() {
        System.out.println("test1()");
    }
}

junit-platform.properties


hoge=HOGE@properties
fuga=FUGA@properties
piyo=PIYO@properties

--hoge, fuga, piyo definiert alles

> java -Dfuga=FUGA@SystemProperty ^
       -Dpiyo=PIYO@SystemProperty ^
       -jar junit-platform-console-standalone-1.5.2.jar ^
       --config piyo=PIYO@ConfigOption ^
...

--fuga und piyo sind in den JVM-Systemeigenschaften angegeben

Ausführungsergebnis


key=hoge, value=HOGE@properties
key=fuga, value=FUGA@SystemProperty
key=piyo, value=PIYO@ConfigOption
test1()

Die folgende Tabelle zeigt die Beziehung zwischen jedem Schlüssel und dem für jede Einstellungsmethode angegebenen Wert. (Der Wert mit * ist der endgültig angenommene Wert)

Schlüssel junit-platform.properties JVM-Systemeigenschaften --config
hoge HOGE@properties *
fuga FUGA@properties FUGA@SystemProperty *
piyo PIYO@properties PIYO@SystemProperty PIYO@ConfigOption *

Es ist ersichtlich, dass der durch "--config" angegebene Wert Priorität hat und der durch "junit-platform.properties" angegebene Wert eine niedrigere Priorität hat.

Behauptung

JUnit5 bietet eine Klasse für Zusicherungen mit dem Namen Zusicherungen.

Hier [assertEquals ()](https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/Assertions.html#assertEquals (java.lang.Object, java.lang). Grundlegende Assertionsmethoden wie Object)) werden bereitgestellt. Außerdem [assertTimeout ()](https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/Assertions.html#assertTimeout(java.time.Duration,org.junit.jupiter). api.function.Executable)) und [assertAll ()](https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/Assertions.html#assertAll(org.junit.jupiter) Es gibt auch einige nützliche Methoden wie .api.function.Executable ...)).

Der Eindruck ist jedoch, dass nur die minimal notwendigen Gegenstände vorbereitet werden.

Mit JUnit5 können Sie jede Assertionsbibliothek eines Drittanbieters verwenden. Mit anderen Worten, wenn Sie AssertJ oder Hamcrest zu den abhängigen Bibliotheken hinzufügen, können Sie JUnit4 hinzufügen. Kann normal verwendet werden.

Das folgende Beispiel zeigt das Hinzufügen von AssertJ zu einer Abhängigkeit.

build.gradle


dependencies {
    ...
    testImplementation "org.assertj:assertj-core:3.11.1"
}

JUnit5Test


package sample.junit5;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.*;

class JUnit5Test {

    @Test
    void test() throws Exception {
        assertThat(10).isEqualTo(8);
    }
}

Ausführungsergebnis


...
.
'-- JUnit Jupiter [OK]
  '-- JUnit5Test [OK]
    '-- test() [X]
          Expecting:
           <10>
          to be equal to:
           <8>
          but was not.
...

Eigentlich denke ich, dass ich diese Behauptungen von Drittanbietern verwenden werde, also werde ich weglassen, wie Standard-Behauptungen verwendet werden.

Referenz

Recommended Posts

Verwendungshinweise zu JUnit5
Verwendungshinweise zu JavaParser
Hinweise zur Verwendung von WatchService
Hinweise zur Verwendung von Spring Shell
Spring Security-Nutzungsnotiz CSRF
Spring Security-Nutzungsnotiz Run-As
Einführung in JUnit (Studiennotiz)
Sicherheit der Verwendungsnotizmethode für Spring Security
Spring Security-Nutzungsnotiz Remember-Me
Hinweise zur Verwendung des Abhängigkeitsmanagement-Plugins
junit
Spring Security-Verwendungsnotiztest
Spring Security-Nutzungsnotiz Authentifizierung / Autorisierung
JCA-Verwendungsprotokoll (Java Encryption Architecture)
Antwortheader für die Verwendung von Spring Security
Sitzungsverwaltung für Spring Security-Nutzungsnotizen
Spring Security-Nutzungsnotiz Basic / Mechanismus
Ganzzahliges Memo
Docker-Memo
Spring Security Usage Memo Domänenobjektsicherheit (ACL)
JUnit 4 Notizen
Lombok Memo
JUnit Geschichte
Dockerfile-Memo
Java-Memo
AWS-Memo
[Persönlich] JUnit5-Memorandum-Memo (in Arbeit)
JUnit Memorandum
irb Verwendung
Memo Stream