Selbstverständlich die neueste Hauptversion des Java-Testframeworks ab 2019.
> 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
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);
}
}
}
InnerTest
-Klasse als innere KlasseEin 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 ]
-cp
zum Klassenpfad hinzu.
--JUnit5-Klassen befinden sich in junit-platform-console-standalone-1.5.2.jar
-- - scan-classpath
, um anzugeben, wo sich die Testklasse befindet, die Sie ausführen möchten
--Testergebnisse werden an die Konsole ausgegeben> 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?)$]
...
Hoge$StaticTest
JUnit5Test
JUnit5Test$StaticTest
JUnit5Tests
JUnit5Tests$StaticTest
TestJUnit5
TestJUnit5$StaticClass
TestJUnit5$StaticTest
Hoge
Hoge$InnerTest
Hoge$StaticClass
JUnit5Test$InnerTest
JUnit5Test$StaticClass
JUnit5Tests$InnerTest
JUnit5Tests$StaticClass
TestJUnit5$InnerTest
-n
oder --include-classname
an (Spezifikation für reguläre Ausdrücke).
-Der Standardwert ist^(Test.*|.+[.$]Test.*|.*Tests?)$
@ Nested
ist erforderlich)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
test {useJUnitPlatform ()}
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
Hoge
*Hoge$StaticClass
*Hoge$StaticTest
JUnit5Test
JUnit5Test$StaticClass
*JUnit5Test$StaticTest
JUnit5Tests
JUnit5Tests$StaticClass
*JUnit5Tests$StaticTest
TestJUnit5
TestJUnit5$StaticClass
TestJUnit5$StaticTest
--Mehr Klassen werden ausgeführt als bei Ausführung mit ConsoleLauncher (mehr Klassen sind mit * gekennzeichnet)InnerTest
) ist jedoch standardmäßig ausgeschlossen, was mit ConsoleLauncher identisch ist.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".
"Hoge" und "StaticClass" wurden ebenfalls ins Visier genommen.
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 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.
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.
Module wie Platform und Jupiter entsprechen der Gruppe in Mavens Klassifikation.
Innerhalb jeder Gruppe gibt es mehrere weitere Artefakte.
** Artefaktabhängigkeiten innerhalb jedes Moduls **
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 **
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.
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.
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
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 **
** 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: <10> but was: <8>" type="org.opentest4j.AssertionFailedError">org.opentest4j.AssertionFailedError: expected: <10> but was: <8>
...
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.
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.
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
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
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
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
named
konditionieren möchtenpackage 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
named
konditionieren möchtenpackage 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)
(
- )
- ,
- &
- |
- !
--Tags können gemäß den zur Laufzeit angegebenen Bedingungen gefiltert werden - include-tag
kann nur auf Tags abzielen, die den Bedingungen entsprechen
-- - exclude-tag
kann nur auf Tags abzielen, die nicht den Bedingungen entsprechenbuild.gradle
...
test {
useJUnitPlatform {
includeTags "foo | !fizz"
}
}
!
:NOT&
:AND|
:ORAusführungsergebnis
> java -jar junit-platform-console-standalone-1.5.2.jar ... --include-tag "(!foo & fizz) | buzz"
...
test2@(bar, fizz)
test3@(fizz)
test4@(buzz)
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.
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
value
das Class
-Objekt der Klasse an, die [MethodOrderer] implementiert (https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/MethodOrderer.html). Machen
--MethodOrder
bietet die Möglichkeit, die Reihenfolge zu steuern, in der Methoden ausgeführt werden.MethodOrder
.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
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.
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
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
value
auf [PER_CLASS]( Geben Sie https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/TestInstance.Lifecycle.html#PER_CLASS an.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()
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) **
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) **
name
angeben.displayName
: Anzeigename der Testmethode@ DisplayName
angegeben ist, wird es verwendet.
--currentRepetition
: Aktuelle Anzahl der Wiederholungen (ab 1)
--totalRepetitions
: Gesamtzahl der Wiederholungenpackage 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)
getCurrentRepetition ()
(1 Start) können Sie die aktuelle Anzahl der Wiederholungen abrufen.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) **
-Die von @ParameterizedTest kommentierte Methode erhält den im Test verwendeten Wert als Argument. Wird ausgeführt während
@ ValueSource
kann statisch Parameter deklarieren, die an Testmethoden mit Attributen wie Strings
und Ints
übergeben werden sollen@ ValueSource
im Beispiel von ↑), wird als Parameter ** source ** bezeichnet.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
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 ())
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
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
MATCH_ALL
kann auf nur Konstanten eingegrenzt werden, die allen Bedingungen entsprechen
--MATCH_ANY
kann auf nur Konstanten eingegrenzt werden, die einer der Bedingungen entsprechenpackage 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
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
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.
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
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]
'
) für Anführungszeichen
--''
wird als leeres Zeichen behandelt, und ein vollständiges Leerzeichen wird als null
behandelt.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]
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]
Class
-Objekt einer Klasse an, die ArgumentsProvider
in value
implementiertpackage 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
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
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) |
irgendeinenum Art (java.lang.Enum Unterklasse) |
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
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.)
[[I
,[Ljava.lang.String;
, etc ...)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]
java.util.Collection
- java.lang.Iterable
- java.util.Iterator
- java.util.stream.Stream
DynamicNode
selbst eine abstrakte Klasse ist, handelt es sich tatsächlich um eine Unterklasse DynamicTest oder [DynamicContainer]( Verwenden Sie eine der folgenden Optionen: https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/DynamicContainer.html)DynamicTest
zu erstellenpackage 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.
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]
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
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
Lifecycle.PER_CLASS
MethodOrderer
außer MethodOrderer.Random
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
PerClassTest
und OrderedTest
laufen jeweils im selben Thread.[^ 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
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
FooTest
und BarTest
laufen in verschiedenen Threads (ForkJoinPool-1-worker-7
und ForkJoinPool-1-worker-3
)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
factor
angegebene Wert mit der Anzahl der Prozessoren (Kerne) in der Ausführungsumgebung multipliziert wird.Faktor
nicht angegeben ist, ist der Standardwert 1.int
)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
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
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
ForkJoinPool
(https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/concurrent/ForkJoinPool.html). #% 3Cinit% 3E (int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean, int, int, int, java.util.function.Predicate, long, java.util.concurrent. Siehe TimeUnit))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
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
value
an
--Synchronisiert zwischen "@ ResourceLock" mit derselben Schlüsselzeichenfolgepackage 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)
mode
von @ ResourceLock
angebenDefaultMethodTest
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()
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
BeforeEachCallback
wird die beforeEach ()
Methode definiert, die vor jedem Test zurückgerufen wird.
AfterEachCallback
definiert eine afterEach ()
Methode, die nach jedem Test zurückgerufen wird.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()
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 | @BeforeEach Behandeln Sie Ausnahmen, die durch Lebenszyklusmethoden wie z. |
TestTemplateInvocationContextProvider | Führt eine vorbereitende Verarbeitung durch, um denselben Test in verschiedenen Kontexten auszuführen. |
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.
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()
evaluExecutionCondition (ExtensionContext)
für jeden Container [^ 6] und jede Methode aufgerufen wird, steuert der Rückgabewert, ob der Zieltest ausgeführt werden soll.Required
verwenden, z. B. html # getRequiredTestMethod ()).[^ 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.
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]
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]
BeforeEachCallback
nicht in Ordnung?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)
-----------------------------------------
supportsParameter ()
wird für jedes Methodenargument aufgerufenParameter.getName ()
beim Kompilieren mit javac
die Option -parameters
hinzugefügt werden muss.
--Wenn es keine Option gibt, lautet der Name "arg0", "arg1".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
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>
TestWatcher
definiert
testDisabled()
testSuccessful()
testAborted()
acceptThat ()
)
testFailed()
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");
}
}
}
UnsupportedOperationException
aus, wenn IllegalStateException
empfangen wirdJUnit5Test
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
AssertionFailedError
) ebenfalls als Ziel ausgewählt wird.
――Wenn Sie vergessen haben, erneut zu werfen, werden Sie den Assertionsfehler quetschen.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
TestExecutionExceptionHandler
wird nicht zurückgerufen, wenn eine Ausnahme in einer Lebenszyklusmethode ausgelöst wirdLifecycleMethodExecutionExceptionHandler
definiertMyTestTemplateInvocationContextProvider
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()]
...
supportsTestTemplate ()
of TestTemplateInvocationContextProvider
wird für jede Methode aufgerufen, für die @ TestTemplate
festgelegt ist.TestTemplateInvocationContext
repräsentiert einen Kontext beim Ausführen eines TestsMyTestTemplateInvocationContextProvider
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]
:
invocationIndex
wird der Index des aktuellen Kontexts übergeben (beginnend mit 1)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]
:
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.
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()
junit-platform.properties
) anzugeben, aber es kann auch in den Systemeigenschaften angegeben werden.Ü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.)
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 Store
s 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.)
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.
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());
}
}
beforeAll ()
undbeforeEach ()
werden zuerst die Informationen von Store
ausgegeben und dann der Wert von hoge
gesetzt.afterEach ()
undafterAll ()
geben die Informationen von Store
so aus, wie sie sind.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
ExtensionContext
(https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/api/extension/ExtensionContext.html#getParent- -) Kann nach Methode erhalten werden
--JupiterEngineExtensionContext
ist der Kontext des übergeordneten Elements (root)ExtensionContext
-) Kann nach Methode erhalten werdenTestTemplateExtensionContext
und DynamicExtensionContext
in der Implementierungsklasse von ExtensionContext
, die hier jedoch weggelassen werden.
--Store
wird für jede Instanz dieser Kontexte gespeichert
--MethodExtensionContext
wird für jede Testmethode generiertMethodExtensionContext
von test1 ()
undtest2 ()
wird an verschiedene Instanzen übergeben.
――Daher ist Store
auch anders.test1 ()
in Store
gespeicherten Informationen nicht von test2 ()
referenziert werden.MyStopwatch
Probleme bei der parallelen Ausführung mit Store
vermeiden.
--BeforeEachCallback
und AfterEachCallback
sind Erweiterungen auf MethodenebeneStore
unterschiedlich.
―― Aus diesem Grund stehen die Daten auch dann nicht in Konflikt, wenn sie parallel ausgeführt werden, und sie können normal funktionieren.
--Informationen, die im übergeordneten Kontext Store
gespeichert sind, können auch aus dem untergeordneten Kontext Store
referenziert werden.
-Und es kann im untergeordneten Kontext überschrieben werdentest1 ()
zu verweisen.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
Store
gespeichert. Wenn ja, am Ende des Store
-Lebenszyklus close (). Die Methode Store.CloseableResource.html # close ()) wird automatisch aufgerufenMyParameterResolver
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
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.
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.
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.
Recommended Posts