5

我有一个 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);
    }
}
4

2 回答 2

2

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();
    }
}
于 2017-02-07T20:24:03.623 回答
0

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

更多信息在这里

于 2014-08-05T19:13:06.380 回答