3

I would like to test methods that have JavaFX scene elements like TextField or DatePicker. I found here How do you unit test a JavaFX controller with JUnit how to run test for JavaFX app and it works fine for me if I have just one test class where Thread is initialized. But what about if I have two classes? I am not able to initialize the same thread with launch method because exception will be thrown. If I will not create thread in second test class there is also another exception. Below you can find test classes and stack trace. I was also thinking about stopping this thread in method with @AfterClass annotation and then starting it in second class again but I do not think it is possible. Does anyone can help me?

FirstTestClass

@RunWith(PowerMockRunner.class)
@PrepareForTest({TextField.class, DatePicker.class})
public class FirstTestClass {

    @BeforeClass
    public static void javaFXInitializer() throws SQLException, InterruptedException {
        Thread thread = new Thread("JavaFX Init Thread") {
            public void run() {
                Application.launch(Main.class);
            }
        };
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(5000);
    }

    @Test
    public void firstTest() {
        TextField textFieldMock = mock(TextField.class);
        DatePicker datePickerMock = mock(DatePicker.class);
        TestClass objectUnderTest = new TestClass();

        when(textFieldMock.getText()).thenReturn("2000");
        when(datePickerMock.getValue()).thenReturn(LocalDate.of(1992,1,1));

        //rest of code to test
    }

}

SecondTestClass

@RunWith(PowerMockRunner.class)
    @PrepareForTest({TextField.class, DatePicker.class})
    public class SecondTestClass {

        @BeforeClass
        public static void javaFXInitializer() throws SQLException, InterruptedException {
            Thread thread = new Thread("JavaFX Init Thread") {
                public void run() {
                    Application.launch(Main.class);
                }
            };
            thread.setDaemon(true);
            thread.start();
            Thread.sleep(5000);
        }

        @Test
        public void firstTest() {
            TextField textFieldMock = mock(TextField.class);
            DatePicker datePickerMock = mock(DatePicker.class);
            TestClass objectUnderTest = new TestClass();

            when(textFieldMock.getText()).thenReturn("5000");
            when(datePickerMock.getValue()).thenReturn(LocalDate.of(1990,1,1));

            //rest of code to test
        }

    }

Exception when I initialize thread in two classes

Exception in thread "JavaFX IncomeTest Init Thread" java.lang.RuntimeException: java.lang.UnsatisfiedLinkError: Native Library C:\Program Files\Java\jdk1.8.0_144\jre\bin\glass.dll already loaded in another classloader
        at com.sun.javafx.tk.quantum.QuantumToolkit.startup(QuantumToolkit.java:267)
        at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:211)
        at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:675)
        at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:695)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
        at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.UnsatisfiedLinkError: Native Library C:\Program Files\Java\jdk1.8.0_144\jre\bin\glass.dll already loaded in another classloader
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1907)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
        at java.lang.Runtime.load0(Runtime.java:809)
        at java.lang.System.load(System.java:1086)
        at com.sun.glass.utils.NativeLibLoader.loadLibraryFullPath(NativeLibLoader.java:201)
        at com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:94)
        at com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:39)
        at com.sun.glass.ui.Application.loadNativeLibrary(Application.java:112)
        at com.sun.glass.ui.Application.loadNativeLibrary(Application.java:120)
        at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
        at com.sun.glass.ui.win.WinApplication$1.run(WinApplication.java:118)
        at com.sun.glass.ui.win.WinApplication$1.run(WinApplication.java:91)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.glass.ui.win.WinApplication.<clinit>(WinApplication.java:91)
        at com.sun.glass.ui.win.WinPlatformFactory.createApplication(WinPlatformFactory.java:39)
        at com.sun.glass.ui.win.WinPlatformFactory.createApplication(WinPlatformFactory.java:36)
        at com.sun.glass.ui.Application.run(Application.java:146)
        at com.sun.javafx.tk.quantum.QuantumToolkit.startup(QuantumToolkit.java:257)
        ... 5 more

Exception when Thread is initialized only in first class

java.lang.ExceptionInInitializerError
        at sun.reflect.GeneratedSerializationConstructorAccessor12.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:45)
        at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
        at org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:14)
        at org.powermock.api.mockito.repackaged.ClassImposterizer.createProxy(ClassImposterizer.java:149)
        at org.powermock.api.mockito.repackaged.ClassImposterizer.imposterise(ClassImposterizer.java:64)
        at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMethodInvocationControl(DefaultMockCreator.java:121)
        at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMock(DefaultMockCreator.java:69)
        at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.mock(DefaultMockCreator.java:46)
        at org.powermock.api.mockito.PowerMockito.mock(PowerMockito.java:141)
        at 
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:326)
        at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
        at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:298)
        at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
        at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:218)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:160)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:134)
        at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
        at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
        at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:136)
        at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
        at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
        at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
        at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
        at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
        at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
        at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
        at javafx.scene.control.Control.<clinit>(Control.java:87)
        ... 50 more
ketrab321
  • 541
  • 2
  • 12
  • 22

1 Answers1

3

Create a class MockApp extends Application and have a static method init(). In that method create a new single thread in which to invoke the JavaFX platform start (basically what you've done already) with an appropriate guard. The guard (a boolean flag) guarantees that the method gets called maximum once. The point is that the thread never terminates, thus allowing you to access the JavaFX platform during the lifetime of all your tests. The catch is you need to add something like:

@BeforeClass
public void init() {
    MockApp.init();
}

for each test class where you use JavaFX since you don't know the order in which tests run.

The approach (with minor changes) can be seen in practice here.

AlmasB
  • 3,377
  • 2
  • 15
  • 17