JavaFX"工具包未在一个测试类中初始化",而不是另外两个; 区别在哪里?

fge*_*fge 3 java testng unit-testing mockito javafx-8

环境:

  • Oracle JDK 1.8u31;
  • Intellij IDEA 14.0.3;
  • Mockito 1.10.17;
  • TestNG 6.8.13.

首先,关于架构的快速目标:JavaFX称之为"控制器"我称之为显示器,并且我有一个控制显示元素的视图类.

所有这些视图都继承了一个公共基类:

public abstract class JavafxView<P, D extends JavafxDisplay<P>>
{
    protected final Node node;
    protected final D display;

    protected JavafxView(final String fxmlLocation)
        throws IOException
    {
        final URL url = JavafxView.class.getResource(fxmlLocation);
        if (url == null)
            throw new IOException(fxmlLocation + ": resource not found");
        final FXMLLoader loader = new FXMLLoader(url);
        node = loader.load();
        display = loader.getController();
    }

    @SuppressWarnings("unchecked")
    @NonFinalForTesting
    public <T extends Node> T getNode()
    {
        return (T) node;
    }

    @NonFinalForTesting
    public D getDisplay()
    {
        return display;
    }
}
Run Code Online (Sandbox Code Playgroud)

起初我开始测试时遇到了问题,因为每次都会得到"工具包没有初始化".然而,在提出问题之后,我获得了一个效果很好的解决方案 ; 我现在可以编写测试来测试行为而没有问题......

除了我现在偶然发现了一个我再次收到此错误的测试:

java.lang.ExceptionInInitializerError
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
    at java.lang.Class.newInstance(Class.java:438)
    at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.constructValue(FXMLLoader.java:1001)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:742)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2701)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2521)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2435)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2403)
    at com.github.fge.grappa.debugger.javafx.JavafxView.<init>(JavafxView.java:24)
    at com.github.fge.grappa.debugger.csvtrace.tabs.matches.JavafxMatchesTabView.<init>(JavafxMatchesTabView.java:24)
    at com.github.fge.grappa.debugger.csvtrace.tabs.matches.JavafxMatchesTabViewTest.init(JavafxMatchesTabViewTest.java:28)
    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:483)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
    at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:564)
    at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:213)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:653)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
    at org.testng.TestRunner.privateRun(TestRunner.java:767)
    at org.testng.TestRunner.run(TestRunner.java:617)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:348)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305)
    at org.testng.SuiteRunner.run(SuiteRunner.java:254)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
    at org.testng.TestNG.run(TestNG.java:1057)
    at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
    at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
    at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:125)
    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:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:270)
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:265)
    at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:540)
    at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:502)
    at javafx.scene.control.Control.<clinit>(Control.java:87)
    ... 47 more
Run Code Online (Sandbox Code Playgroud)

JavafxView.java:24 是这一行:

node = loader.load();
Run Code Online (Sandbox Code Playgroud)

然而,这并不是最奇怪的.

我有两个其他测试类测试另外两个测试类JavafxView,当我单独运行测试类时,它们没有问题; 只有这个新的测试不...

...但是,如果我运行整个测试套件而不是仅测试那么测试就会超过!

以下是测试的完整源代码:

public class JavafxMatchesTabViewTest
{
    private JavafxMatchesTabView view;
    private MatchesTabDisplay display;

    @BeforeMethod
    public void init()
        throws IOException
    {
        view = new JavafxMatchesTabView();
        display = view.getDisplay();
    }

    @Test
    public void showMatchesTest()
    {
        final List<MatchStatistics> oldStats = Arrays.asList(
            mock(MatchStatistics.class),
            mock(MatchStatistics.class)
        );

        final List<MatchStatistics> newStats = Arrays.asList(
            mock(MatchStatistics.class),
            mock(MatchStatistics.class)
        );

        final TableView<MatchStatistics> tableView = spy(new TableView<>());
        display.matchesTable = tableView;

        final ObservableList<MatchStatistics> tableData = tableView.getItems();
        final ObservableList<TableColumn<MatchStatistics, ?>> sortOrder
            = tableView.getSortOrder();

        tableData.setAll(oldStats);
        sortOrder.clear();

        view.showMatches(newStats);

        assertThat(tableData).containsExactlyElementsOf(newStats);
        assertThat(sortOrder).containsExactly(display.nrCalls);
        verify(tableView).sort();
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,呃,我该如何解决这个问题呢?乍一看似乎解决方案可能是我将这个习惯"移植" MockMaker到TestNG的@BeforeClass......除了我不知道我怎么能真正做到这一点:/

fge*_*fge 5

好吧,呃,这比我想象的要简单,而且通常情况下我只是在问过这个问题后才找到解决方案......

无论如何,解决方案是创建一个抽象基类,为您初始化工具包,它就像这样"简单":

@Test
public abstract class JavafxViewTest
{
    @BeforeClass
    public static void initToolkit()
        throws InterruptedException
    {
        final CountDownLatch latch = new CountDownLatch(1);
        SwingUtilities.invokeLater(() -> {
            new JFXPanel(); // initializes JavaFX environment
            latch.countDown();
        });

        // That's a pretty reasonable delay... Right?
        if (!latch.await(5L, TimeUnit.SECONDS))
            throw new ExceptionInInitializerError();
    }
}
Run Code Online (Sandbox Code Playgroud)