如何使用JUnit对JavaFX控制器进行unit testing
什么是初始化JavaFX运行时的正确方法,以便您可以使用并发工具和Platform.runLater(Runnable)
进行unit testing(使用JUnit)控制器?
从@BeforeClass
方法调用Application.launch(...)
导致死锁。 如果未调用Application.launch(...)
则抛出以下错误:
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)
跟进:这是我根据@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...
从@BeforeClass
调用launch()
是一种正确的方法。 请注意, launch()
不会将控制权返回给调用代码。 所以你必须将它包装到new Thread(...).start()
。
我发现这个工作,…但只有在启动JavaFX应用程序线程后添加Thread.sleep(500)。 据推测,需要一些时间才能完成FX环境并准备好(我的MacBook Pro视网膜上约200ms)
@BeforeClass public static void setUpClass() throws InterruptedException { // Initialise Java FX System.out.printf("About to launch FX App\n"); Thread t = new Thread("JavaFX Init Thread") { public void run() { Application.launch(AsNonApp.class, new String[0]); } }; t.setDaemon(true); t.start(); System.out.printf("FX App thread started\n"); Thread.sleep(500); }