Écrire des tests pour les applications JavaFX avec TestFX

Aperçu

Décrit comment tester le code JavaFX à l'aide de TestFX, une bibliothèque de test pour JavaFX.


Qu'est-ce que TestFX?

Une bibliothèque pour implémenter des tests dans JavaFX. Vous pouvez utiliser cette bibliothèque pour implémenter des tests d'interface utilisateur ainsi que des tests unitaires. La licence est EUPL v1.1. Cette fois, je vais essayer d'utiliser 4 séries. Depuis janvier 2017, lorsque cet article a été écrit, `` 4.0.5-alpha '' est le dernier.

Pourquoi utiliser une bibliothèque?

Lorsque j'implémente et exécute un test qui inclut une application JavaFX ou une classe JavaFX à l'aide de JUnit standard, j'obtiens `` java.lang.ExceptionInInitializerError '' comme indiqué ci-dessous.

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)

... Omis ...

	at org.mockito.Mockito.mock(Mockito.java:1120)
	at jp.toastkid.loto6.Loto6ControllerTest.setUpTarget(Loto6ControllerTest.java:38)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

... Omis ...

	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)

... Omis ...

	at javafx.scene.control.Control.<clinit>(Control.java:87)
	... 37 more

Caused by: java.lang.IllegalStateException:Boîte à outils non initialisée.[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)Il semble que cela puisse être implémenté en utilisant la méthode décrite dans, mais c'est un peu compliqué ... J'ai donc décidé d'utiliser la bibliothèque existante.



## Environnement et autres
 Cette mise en œuvre et cette confirmation ont été effectuées dans l'environnement suivant.

 | 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

----

# Présentation de la bibliothèque
 Pour Gradle, tout ce que vous avez à faire est d'ajouter une ligne de dépendances testCompile aux dépendances.


#### **`build.Correction de gradle`**
```groovy

    testCompile group: 'org.testfx', name: 'testfx-junit', version: '4.0.5-alpha'

Alternativement, ce format est très bien.

build.Correction de gradle


    testCompile 'org.testfx:testfx-junit:4.0.5-alpha'

Si vous utilisez un outil de compilation autre que Gradle, veuillez vérifier sur mvnrepository.com.

Bibliothèque dépendante de TestFX

Les goyaves etc. sont inclus en tant que dépendances.

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

Mise en œuvre des tests

La classe de test hérite de la classe org.testfx.framework.junit.ApplicationTest. Il semble que jusqu'à la 3e série a été nommée GuiTest ''. Dans la 4ème série, il a été renommé ApplicationTest. Cette classe doit implémenter la méthode public void start (Stage stage) throws Exception ''. Dans TestFX, le style principal consiste à écrire des tests pour la classe Controller.

Cette fois, je vais implémenter le test dans application JavaFX qui exécute des scripts que j'ai créée plus tôt.

testfx_ss1.png

J'ai un écran comme celui-ci. Le code de Groovy entré par l'utilisateur est changé en ScriptEngine. api / javax / script / ScriptEngine.html), et cliquez sur le bouton qui dit exécuter pour afficher le résultat de l'exécution du script. Le côté gauche de l'écran est le CodeArea (TextArea) qui accepte l'entrée de code de l'utilisateur, et le côté gauche est le CodeArea (TextArea) qui affiche le résultat de l'exécution.

Mise en œuvre de la méthode de démarrage (étape étape)

Initialisez et affichez l'interface graphique dans cette méthode. Il est exécuté avant la méthode avec @Before. Cette fois, je décrirai le processus d'initialisation avant d'afficher l'écran en fonction de l'implémentation de la classe qui appelle Controller.

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

Comme mentionné ci-dessus, nous chargeons FXML, récupérons le contrôleur, initialisons la scène et la scène, et enfin affichons la scène.

Méthode d'essai vide

Nous avons également une méthode de test vide pour exécuter JUnit's Runner.

Méthode d'essai vide


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

Courir

Lorsque vous exécutez le test JUnit, vous verrez l'écran de l'application comme indiqué ci-dessous. Puisqu'aucune autre méthode de test n'a encore été mise en œuvre, elle n'apparaîtra que pendant un moment, puis disparaîtra immédiatement.

testfx_ss1.png

Cette fois, j'expliquerai brièvement la demande d'écriture du test. Il a une fonction pour exécuter le code Groovy entré par l'utilisateur dans ScriptEngine et cliquez sur le bouton intitulé exécuter pour afficher le résultat de l'exécution du script. Le côté gauche de l'écran est le CodeArea (TextArea) qui accepte l'entrée de code de l'utilisateur, et le côté gauche est le CodeArea (TextArea) qui affiche le résultat de l'exécution.

Ajouter une méthode de test

Ensuite, ajoutons le code pour le test de l'interface utilisateur.

Accepter l'entrée

Essayez d'entrer Hello World de Groovy.

Entrez le script dans la zone de texte sur la gauche


@Test
public void test() throws InterruptedException {
    final CodeArea input = (CodeArea) lookup("#scripterInput").query();
    final String text = "println 'Hello world.'";
    Platform.runLater(() -> input.replaceText(text));
}

Recherche de nœud

Les nœuds inclus dans la scène affichée peuvent être recherchés par ID. L'ID peut être défini dans FXML ou spécifié par setId dans la classe Node. Un exemple de définition dans FXML est présenté ci-dessous.

Exemple de définition dans FXML


<CodeArea fx:id="scripterInput" prefHeight="450.0" prefWidth="500.0">

scripterinputRecherchez le nœud avec l'ID nommé codearea, transtypez-le dans la classe d'implémentation codearea et utilisez-le dans les tests suivants.

Lookup->cast


final CodeArea input = (CodeArea) lookup("#scripterInput").query();

Sur la classe de test JUnit, lors de la définition d'une valeur pour JavaFX Control, elle échouera à moins que vous n'utilisiez `` Platform.runLater ''.

Platform.runLater(() -> input.replaceText(text));

Courir

Lorsque vous exécutez le test, l'écran s'affiche avec le script saisi comme indiqué ci-dessous.

testfx_ss2.png

Exécution des événements de l'interface utilisateur

Le code pour obtenir le Button qui dit exécuter avec Lookup et exécuter l'événement set peut être écrit sur une ligne ci-dessous.

Lookup->query->fireEvent


Platform.runLater(() -> lookup("#runButton").query().fireEvent(new ActionEvent()));

Puisque le bouton d'exécution est spécifié dans FXML pour exécuter une méthode appelée runScript de la classe Controller, cette méthode est exécutée et CodeArea (TextArea) sur le côté droit de l'écran est le résultat de l'exécution du script Hello world ''. `` s'affiche.

testfx_ss3.png

Vérification de la valeur

Tout ce que vous avez à faire est de vérifier la valeur avec la méthode assert.

Code de test final


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

point important

L'exécution de la méthode est asynchrone à l'intérieur et à l'extérieur de Platform.runLater, donc si vous souhaitez inspecter la valeur de Node causée par les modifications apportées à Node dans Platform.runLater, vous devez l'écrire dans Platform.runLater.

Comme exemple d'échec, le code ci-dessous échoue toujours car la vérification avec assertEquals est effectuée avant input.replaceText (text) in Platform.runLater.

Cas où l'inspection échoue


Platform.runLater(() -> input.replaceText(text));
assertEquals(text, input.getText());

Tester avec Mock

Vous pouvez également écrire le même code en utilisant la Whitebox de Mockito sans utiliser la recherche de TestFX (Remarque: le contrôleur doit avoir son propre objet Node).

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

L'avantage d'utiliser Mockito est que vous pouvez exécuter des tests sans afficher l'écran. Cette méthode est recommandée pour ceux qui sont ennuyés par l'affichage de l'écran à chaque fois.


Résumé

Jusqu'à présent, nous avons brièvement décrit comment écrire des tests pour les applications JavaFX à l'aide de TestFX. Vous pourriez penser que c'est ennuyeux de voir l'écran chaque fois que vous exécutez un test. C'est ennuyant. Si vous souhaitez tester sans afficher l'écran, il est préférable d'utiliser Mock pour créer le test. Même dans ce cas, TestFX facilite l'écriture de tests car vous n'avez pas à gérer vous-même les parties liées aux threads de JavaFX. Je ne l'ai pas présenté cette fois, mais si vous voulez écrire un test unitaire pour votre propre composant JavaFX, vous pouvez utiliser ce TestFX.


référence

article

Il y a déjà beaucoup d'informations sur TestFX en anglais et en japonais.

Anglais

Japonais

Il y a beaucoup d'informations sur le 3ème système.

code

Recommended Posts

Écrire des tests pour les applications JavaFX avec TestFX
[Swift / Pour les débutants] Écrivez intelligemment avec l'inférence de type
HelloFX avec JavaFX
Écrivez rapidement un test RestController avec Spring Boot + Spock
Générer des données factices pour divers tests avec Faker (java)
Préparer l'environnement pour java11 et javaFx avec Ubuntu 18.4
Joyeux Noël avec JavaFX !!
Écrivez solidement avec PhpStorm
[Java] Exemple de projet de développement d'applications Web avec Spring Boot