如何对JavaFX应用程序启动进行单元测试

时间:2014-07-20 15:16:54

标签: java unit-testing javafx javafx-8 testfx

我有一个JavaFX应用程序,我想测试它是否启动。我该怎么做呢?是否可以只使用JUnit,或者TestFX可以帮助我吗?

我的主要问题是:如何在(成功)启动后立即关闭应用程序?

示例应用程序类:

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

2 个答案:

答案 0 :(得分:2)

由于Application.launch方法在应用程序退出之前不会返回,因此通过调用Platform.exit或所有应用程序窗口都已关闭,因此您必须将其包装到另一个线程中终止它。

如果在JavaFX应用程序启动后立即调用Platform.exit,您将收到IllegalStateException。如果等待一段时间以便可以初始化JavaFX应用程序然后调用Platform.exit,那么JavaFX应用程序和包装器线程都将被终止而不会完成或抛出任何异常。我无法通过使用Platform.exit找到解决方法。

但是,我设法通过使用Thread.interrupt来完成它。只需在包装线程中运行JavaFX应用程序,等待一段时间,然后中断包装线程。这样,JavaFX应用程序将被中断并抛出InterruptedException。如果它没有抛出,则启动JavaFX应用程序时会出现问题。

请注意,等待JVM启动JavaFX应用程序可能需要更长的时间,因此此方法无法保证JavaFX应用程序在正确启动后中断,这可能会导致错误的负面情况。

测试类

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应用程序类

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

答案 1 :(得分:0)

假设primaryStage是唯一的开放阶段,当您调用primaryStage.hide()时,JavaFX线程将自动关闭。这是因为默认情况下JavaFX设置为在隐藏所有阶段时关闭,可以通过调用Platform.setImplicitExit(false)来更改。

更多信息here