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);
}
}
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.
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);
}
}
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With