Schreiben Sie Tests für JavaFX-Anwendungen mit TestFX

Überblick

Beschreibt das Testen von JavaFX-Code mit TestFX, einer Testbibliothek für JavaFX.


Was ist TestFX?

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.

Warum eine Bibliothek benutzen?

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.

TestFX-abhängige Bibliothek

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

Testimplementierung

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.

testfx_ss1.png

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.

Implementierung der Startmethode (Stage Stage)

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&nbsp;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.

Testmethode leeren

Wir haben auch eine leere Testmethode, um JUnits Runner auszuführen.

Testmethode leeren


@Test
public void test() {
    // NOP.
}

Lauf

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.

testfx_ss1.png

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.

Testmethode hinzufügen

Als nächstes fügen wir den Code für den UI-Test hinzu.

Eingaben akzeptieren

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));
}

Knotensuche

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">

scripterinputSuchen 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));

Lauf

Wenn Sie den Test ausführen, wird der Bildschirm mit dem wie unten gezeigt eingegebenen Skript angezeigt.

testfx_ss2.png

UI-Ereignisse ausführen

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.

testfx_ss3.png

Überprüfen Sie den Wert

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()
        );
    });
}

wichtiger Punkt

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());

Testen mit Mock

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.


Zusammenfassung

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.


Referenz

Artikel

Es gibt bereits viele Informationen zu TestFX auf Englisch und Japanisch.

Englisch

japanisch

Es gibt viele Informationen zum 3. System.

Code

Recommended Posts

Schreiben Sie Tests für JavaFX-Anwendungen mit TestFX
[Swift / Für Anfänger] Schreiben Sie intelligent mit Typinferenz
HelloFX mit JavaFX
Schreiben Sie schnell RestController-Tests mit Spring Boot + Spock
Generieren Sie Dummy-Daten für verschiedene Tests mit Faker (Java)
Bereiten Sie die Umgebung für Java11 und JavaFx mit Ubuntu 18.4 vor
Frohe Weihnachten mit JavaFX !!
Schreiben Sie solide mit PhpStorm
[Java] Beispielprojekt zum Entwickeln von Webanwendungen mit Spring Boot