Beschreibt das Testen von JavaFX-Code mit TestFX, einer Testbibliothek für JavaFX.
Eine Bibliothek zum Implementieren von Tests in JavaFX. Mit dieser Bibliothek können Sie sowohl UI-Tests als auch Unit-Tests implementieren. Die Lizenz lautet EUPL v1.1. Dieses Mal werde ich versuchen, 4 Serien zu verwenden. Ab Januar 2017, als dieser Artikel geschrieben wurde, ist `` `4.0.5-alpha``` der neueste.
Wenn ich einen Test implementiere und ausführe, der eine JavaFX-Anwendung oder eine JavaFX-Klasse mit regulärem JUnit enthält, erhalte ich `` `java.lang.ExceptionInInitializerError``` wie unten gezeigt.
StackTrace
java.lang.ExceptionInInitializerError
at sun.reflect.GeneratedSerializationConstructorAccessor2.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:40)
at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:59)
at org.mockito.internal.creation.jmock.ClassImposterizer.createProxy(ClassImposterizer.java:128)
... weggelassen ...
at org.mockito.Mockito.mock(Mockito.java:1120)
at jp.toastkid.loto6.Loto6ControllerTest.setUpTarget(Loto6ControllerTest.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
... weggelassen ...
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
... weggelassen ...
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 37 more
Caused by: java.lang.IllegalStateException:Toolkit nicht initialisiert.[How do you unit test a JavaFX controller with JUnit](http://stackoverflow.com/questions/11385604/how-do-you-unit-test-a-javafx-controller-with-junit)Es scheint, dass es mit der in beschriebenen Methode implementiert werden kann, aber es ist etwas kompliziert ... Also habe ich beschlossen, die vorhandene Bibliothek zu verwenden.
## Umwelt und andere
Diese Implementierung und Bestätigung wurde in der folgenden Umgebung durchgeführt.
| Java SE | 1.8.0_102
|:---|:---
| OS | Windows 10
| Eclipse | 4.5
| JFoenix | 1.0.0
| JUnit | 4.11
| Mockito | 1.9.5
| TestFX | 4.0.5-alpha
----
# Vorstellung der Bibliothek
Für Gradle müssen Sie lediglich eine Reihe von testCompile-Abhängigkeiten zu Abhängigkeiten hinzufügen.
#### **`build.Gradle reparieren`**
```groovy
testCompile group: 'org.testfx', name: 'testfx-junit', version: '4.0.5-alpha'
Alternativ ist dieses Format in Ordnung.
build.Gradle reparieren
testCompile 'org.testfx:testfx-junit:4.0.5-alpha'
Wenn Sie ein anderes Build-Tool als Gradle verwenden, überprüfen Sie bitte mvnrepository.com.
Guave usw. sind als Abhängigkeiten enthalten.
dependencies
org.testfx:testfx-junit:4.0.5-alpha
\--- org.testfx:testfx-core:4.0.5-alpha
+--- com.google.guava:guava:20.0
+--- org.hamcrest:hamcrest-core:1.3
\--- com.google.code.findbugs:annotations:3.0.1u2
+--- net.jcip:jcip-annotations:1.0
\--- com.google.code.findbugs:jsr305:3.0.1
Die Testklasse erbt von der Klasse `org.testfx.framework.junit.ApplicationTest```. Es scheint, dass bis zur 3. Serie "GuiTest" genannt wurde. In der 4. Serie wurde es in ApplicationTest umbenannt. Diese Klasse muss die Methode
`public void start (Stage stage) throw Exception``` implementieren.
In TestFX besteht der Hauptstil darin, Tests für die Controller-Klasse zu schreiben.
Dieses Mal werde ich den Test, den ich zuvor erstellt habe, in JavaFX-Anwendung, die Skripte ausführt implementieren.
Ich habe einen Bildschirm wie diesen. Der vom Benutzer eingegebene Code von Groovy wird in ScriptEngine geändert. api / javax / script / ScriptEngine.html) und klicken Sie auf die Schaltfläche Ausführen, um das Ausführungsergebnis des Skripts anzuzeigen. Die linke Seite des Bildschirms ist die CodeArea (TextArea), die die Codeeingabe vom Benutzer akzeptiert, und die linke Seite ist die CodeArea (TextArea), die das Ausführungsergebnis anzeigt.
Initialisieren und zeigen Sie die GUI innerhalb dieser Methode an. Es wird vor der Methode mit @Before ausgeführt. Dieses Mal werde ich den Initialisierungsprozess beschreiben, bevor der Bildschirm gemäß der Implementierung der Klasse angezeigt wird, die Controller aufruft.
Test class
@Override
public void start(final Stage stage) throws Exception {
try {
final FXMLLoader loader
= new FXMLLoader(getClass().getClassLoader().getResource("scenes/Main.fxml"));
final VBox loaded = (VBox) loader.load();
controller = (Controller) loader.getController();
final Scene scene = new Scene(loaded);
stage.setScene(scene);
} catch (final IOException e) {
LOGGER.error("Scene Reading Error", e);
}
stage.show();
}
Wie oben erwähnt, laden wir FXML, holen den Controller, initialisieren die Szene und die Bühne und zeigen schließlich die Bühne an.
Wir haben auch eine leere Testmethode, um JUnits Runner auszuführen.
Testmethode leeren
@Test
public void test() {
// NOP.
}
Wenn Sie den JUnit-Test ausführen, wird der unten gezeigte Anwendungsbildschirm angezeigt. Da noch keine andere Testmethode implementiert wurde, erscheint sie nur für einen Moment und verschwindet dann sofort.
Dieses Mal werde ich kurz den Antrag zum Schreiben des Tests erläutern. Es hat eine Funktion, um den vom Benutzer in ScriptEngine eingegebenen Groovy-Code auszuführen und auf die Schaltfläche Ausführen zu klicken, um das Ausführungsergebnis des Skripts anzuzeigen. Die linke Seite des Bildschirms ist die CodeArea (TextArea), die die Codeeingabe vom Benutzer akzeptiert, und die linke Seite ist die CodeArea (TextArea), die das Ausführungsergebnis anzeigt.
Als nächstes fügen wir den Code für den UI-Test hinzu.
Versuchen Sie, Groovys Hello World zu betreten.
Geben Sie das Skript in den Textbereich links ein
@Test
public void test() throws InterruptedException {
final CodeArea input = (CodeArea) lookup("#scripterInput").query();
final String text = "println 'Hello world.'";
Platform.runLater(() -> input.replaceText(text));
}
In der angezeigten Szene enthaltene Knoten können anhand der ID nachgeschlagen werden. Die ID kann in FXML definiert oder in der Knotenklasse setId angegeben werden. Ein Beispiel für die Definition in FXML ist unten dargestellt.
Definitionsbeispiel in FXML
<CodeArea fx:id="scripterInput" prefHeight="450.0" prefWidth="500.0">
scripterinput
Suchen Sie den Knoten mit der ID codearea, wandeln Sie ihn in die Implementierungsklasse codearea um und verwenden Sie ihn in nachfolgenden Tests.
Lookup->cast
final CodeArea input = (CodeArea) lookup("#scripterInput").query();
In der JUnit-Testklasse schlägt das Festlegen eines Werts für JavaFX Control fehl, es sei denn, Sie verwenden "Platform.runLater".
Platform.runLater(() -> input.replaceText(text));
Wenn Sie den Test ausführen, wird der Bildschirm mit dem wie unten gezeigt eingegebenen Skript angezeigt.
Der Code zum Abrufen der Schaltfläche "Mit Lookup ausführen und das festgelegte Ereignis ausführen" kann in eine Zeile darunter geschrieben werden.
Lookup->query->fireEvent
Platform.runLater(() -> lookup("#runButton").query().fireEvent(new ActionEvent()));
Da die Ausführungsschaltfläche in FXML angegeben ist, um eine Methode namens runScript der Controller-Klasse auszuführen, wird diese Methode ausgeführt, und die CodeArea (TextArea) auf der rechten Seite des Bildschirms ist das Ergebnis der Ausführung des Skripts "Hallo Welt" `` wird angezeigt.
Sie müssen lediglich den Wert mit der assert-Methode überprüfen.
Endgültiger Testcode
@Test
public void test() throws InterruptedException {
final CodeArea input = (CodeArea) lookup("#scripterInput").query();
final String text = "println 'Hello world.'";
Platform.runLater(() -> {
input.replaceText(text);
assertEquals(text, input.getText());
lookup("#runButton").query().fireEvent(new ActionEvent());
assertEquals(
"Hello world.",
((CodeArea) lookup("#scripterOutput").query()).getText().trim()
);
});
}
Die Methodenausführung ist innerhalb und außerhalb von Platform.runLater asynchron. Wenn Sie also den Wert von Node überprüfen möchten, der durch Änderungen an Node in Platform.runLater verursacht wurde, müssen Sie ihn in Platform.runLater schreiben.
Als Beispiel für einen Fehler schlägt der folgende Code immer fehl, da die Prüfung mit assertEquals vor `input.replaceText (text)`
in Platform.runLater durchgeführt wird.
Fall, in dem die Inspektion fehlschlägt
Platform.runLater(() -> input.replaceText(text));
assertEquals(text, input.getText());
Sie können denselben Code auch mit Mockitos Whitebox schreiben, ohne TestFXs Lookup zu verwenden (Hinweis: Der Controller muss über ein eigenes Node-Objekt verfügen).
@Test
public void test_mockito() throws InterruptedException {
final CodeArea input = (CodeArea) Whitebox.getInternalState(controller, "scripterInput");
final String text = "println 'Hello world.'";
Platform.runLater(() -> {
input.replaceText(text);
assertEquals(text, input.getText());
((Button) Whitebox.getInternalState(controller, "runButton")).fireEvent(new ActionEvent());
assertEquals(
"Hello world.",
((CodeArea) Whitebox.getInternalState(controller, "scripterOutput")).getText().trim()
);
});
}
Der Vorteil der Verwendung von Mockito besteht darin, dass Sie Tests ausführen können, ohne den Bildschirm anzuzeigen. Diese Methode wird für diejenigen empfohlen, die sich darüber ärgern, dass der Bildschirm jedes Mal angezeigt wird.
Bisher haben wir kurz beschrieben, wie Sie Tests für JavaFX-Anwendungen mit TestFX schreiben. Es könnte ärgerlich sein, den Bildschirm jedes Mal zu sehen, wenn Sie einen Test ausführen. Es ist nervig. Wenn Sie testen möchten, ohne den Bildschirm anzuzeigen, ist es besser, Mock zum Erstellen des Tests zu verwenden. Selbst in diesem Fall erleichtert TestFX das Schreiben von Tests, da Sie sich nicht selbst mit den threadbezogenen Teilen von JavaFX befassen müssen. Ich habe es diesmal nicht eingeführt, aber wenn Sie einen Komponententest für Ihre eigene JavaFX-Komponente schreiben möchten, können Sie diesen TestFX verwenden.
Es gibt bereits viele Informationen zu TestFX auf Englisch und Japanisch.
Es gibt viele Informationen zum 3. System.
Recommended Posts