Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test that a JavaFX application launches

I've got a JavaFX application, and I want to test if it launches or not. How would I go about doing that? Is it possible with just JUnit, or can TestFX help me in that?

My main issue is: How do I shut down the application right after it has (succesfully) launched?

Example application class:

public class MovieDB extends Application {
    @Override
    public void start(final Stage primaryStage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(MovieDBController.class.getResource("MovieDB.fxml"), ResourceBundle.getBundle("bundles/bundle", new Locale("en")));
        Parent root = fxmlLoader.load();

        Scene scene = new Scene(root, 1024, 768);

        StyleManager.getInstance().addUserAgentStylesheet(getClass().getResource("/css/MovieDB.css").getPath());

        primaryStage.setTitle("MovieDB");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
like image 705
skiwi Avatar asked Nov 01 '22 20:11

skiwi


1 Answers

Since the Application.launch method does not return until the application has exited, either via a call to Platform.exit or all of the application windows have been closed so you have to wrap it into another thread in order to terminate it.

If you call Platform.exit right after the JavaFX application launches you will get IllegalStateException. If you wait for a while so your JavaFX application can be initialized and then call Platform.exit, both your JavaFX application and your wrapper thread will be terminated without completing or throwing any exception. I couldn't find a way to work that out by using Platform.exit.

However, I managed to do it by using Thread.interrupt. Simply run your JavaFX application inside a wrapper thread, wait for a while and then interrupt your wrapper thread. This way the JavaFX application will be interrupted and throw InterruptedException. If it does not throw then there is an issue launching your JavaFX application.

Note that it may take longer than you wait for JVM to launch JavaFX application so this method does not guarantee that the JavaFX application is interrupted after it is properly launched which might result in a false negative situation.

Test Class

import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static org.junit.Assert.assertTrue;
import org.junit.Test;

public class JavaFXTest {

    // Wrapper thread updates this if
    // the JavaFX application runs without a problem.
    // Declared volatile to ensure that writes are visible to every thread.
    private volatile boolean success = false;

    /**
     * Test that a JavaFX application launches.
     */
    @Test
    public void testMain() {
        Thread thread = new Thread() { // Wrapper thread.
            @Override
            public void run() {
                try {
                    Application.launch(JavaFXTest.class); // Run JavaFX application.
                    success = true;
                } catch(Throwable t) {
                    if(t.getCause() != null && t.getCause().getClass().equals(InterruptedException.class)) {
                        // We expect to get this exception since we interrupted
                        // the JavaFX application.
                        success = true;
                        return;
                    }
                    // This is not the exception we are looking for so log it.
                    Logger.getLogger(JavaFXTest.class.getName()).log(Level.SEVERE, null, t);
                }
            }
        };
        thread.setDaemon(true);
        thread.start();
        try {
            Thread.sleep(3000);  // Wait for 3 seconds before interrupting JavaFX application
        } catch(InterruptedException ex) {
            // We don't care if we wake up early.
        }
        thread.interrupt();
        try {
            thread.join(1); // Wait 1 second for our wrapper thread to finish.
        } catch(InterruptedException ex) {
            // We don't care if we wake up early.
        }
        assertTrue(success);
    }
}

JavaFX Application Class

import java.io.IOException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class JavaFX extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        primaryStage.setTitle("JavaFX");
        Label label = new Label("Hello World!");
        StackPane root = new StackPane();
        root.getChildren().add(label);
        primaryStage.setScene(new Scene(root, 250, 250));
        primaryStage.show();
    }
}
like image 163
Root G Avatar answered Nov 12 '22 14:11

Root G