Come si esegue il test dell’unità con un controller JavaFX con JUnit

Qual è il modo corretto di inizializzare il runtime JavaFX in modo da poter testare unitamente (con JUnit) i controllori che fanno uso delle funzionalità di concorrenza e Platform.runLater(Runnable) ?

Chiamando Application.launch(...) dal metodo @BeforeClass ottiene un @BeforeClass . Se Application.launch(...) non viene chiamato, viene generato il seguente errore:

 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) 

Follow-up: questo è il motivo che ho utilizzato in base alla raccomandazione di @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... 

Chiamare launch() da @BeforeClass è un approccio corretto. Basta notare che launch() non restituisce il controllo al codice chiamante. Quindi devi avvolgerlo in una new Thread(...).start() .

Ho trovato questo funziona, … ma solo dopo aver aggiunto un Thread.sleep (500) dopo aver avviato il thread dell’applicazione JavaFX. Presumibilmente ci vuole un po ‘di tempo per avere l’ambiente FX pronto e pronto (circa 200ms sulla mia retina di MacBook Pro)

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