Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

What's the proper way of initializing the JavaFX runtime so you can unit test (with JUnit) controllers that make use of the concurrency facilities and Platform.runLater(Runnable)?

Calling Application.launch(...) from the @BeforeClass method results in a dead lock. If Application.launch(...) is not called then the following error is thrown:


java.lang.IllegalStateException: Toolkit not initialized
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:121)
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:116)
    at javafx.application.Platform.runLater(Platform.java:52)
    at javafx.concurrent.Task.runLater(Task.java:1042)
    at javafx.concurrent.Task.updateMessage(Task.java:987)
    at com.xyz.AudioSegmentExtractor.call(AudioSegmentExtractor.java:64)
    at com.xyz.CompletionControllerTest.setUp(CompletionControllerTest.java:69)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Followup: this is the motif I've been using based on recommendation by @SergeyGrinev.

... // Inside test class

public static class AsNonApp extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        // noop
    }
}

@BeforeClass
public static void initJFX() {
    Thread t = new Thread("JavaFX Init Thread") {
        public void run() {
            Application.launch(AsNonApp.class, new String[0]);
        }
    };
    t.setDaemon(true);
    t.start();
}
... // controller tests follow...
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
261 views
Welcome To Ask or Share your Answers For Others

1 Answer

Calling launch() from @BeforeClass is a correct approach. Just note that launch() doesn't return control to calling code. So you have to wrap it into new Thread(...).start().

A 7 years later update:

Use TestFX! It will take care of launching in a proper way. E.g. you can extend your test from a TestFX's ApplicaionTest class and just use the same code:

public class MyTest extends ApplicationTest {

@Override
public void start (Stage stage) throws Exception {
    FXMLLoader loader = new FXMLLoader(
            getClass().getResource("mypage.fxml"));
    stage.setScene(scene = new Scene(loader.load(), 300, 300));
    stage.show();
}

and write tests like that:

@Test
public void testBlueHasOnlyOneEntry() {
    clickOn("#tfSearch").write("blue");
    verifyThat("#labelCount", hasText("1"));
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...