JavaFX "toolkit not initialized" 在一项测试中 class 而不是另外两项;区别在哪里?
JavaFX "toolkit not initialized" in one test class but not two others; where is the difference?
环境:
- 甲骨文 JDK 1.8u31;
- Intellij IDEA 14.0.3;
- Mockito 1.10.17;
- TestNG 6.8.13.
首先,关于架构的快速目标:JavaFX 称之为 "controller" 我称之为显示,我有一个视图 class 控制显示的元素。
所有这些观点都继承了一个共同的基础class:
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;
}
}
起初我在开始测试时遇到了问题,因为我每次都会得到 "toolkit not initialized"。但是,在问完这个问题后,我 效果很好;我现在可以毫无问题地编写测试来测试行为...
只是我现在偶然发现了一个测试,在这个测试中我又遇到了这个错误:
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
JavafxView.java:24
是这一行:
node = loader.load();
不过,这还不是最奇怪的。
我有另外两个测试 classes 测试另外两个 JavafxView
当我单独 运行 测试 classes 时它们没有问题;只有这个新测试没有...
... 但是如果我 运行 整个 测试套件而不是仅那个测试那么测试成功!
这里是测试的完整源代码:
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();
}
}
那么,呃,我该如何解决这个问题?乍一看,解决方案似乎是我 "port" 自定义 MockMaker
到 TestNG 的 @BeforeClass
... 除了我不明白我怎么能真正这样做:/
嗯,我想我们必须将相同的方法应用于 @BeforeClass
and/or @BeforeMethod
.. 就像这样(未经测试):
private boolean jfxIsSetup;
private void doOnJavaFXThread(Runnable pRun) throws RuntimeException {
if (!jfxIsSetup) {
setupJavaFX();
jfxIsSetup = true;
}
final CountDownLatch countDownLatch = new CountDownLatch(1);
Platform.runLater(() -> {
pRun.run();
countDownLatch.countDown();
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
protected void setupJavaFX() throws RuntimeException {
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> {
new JFXPanel(); // initializes JavaFX environment
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@BeforeMethod
public void init() throws IOException
{
AtomicReference<JavafxMatchesTabView> tabView = new AtomicReference<>();
AtomicReference<MatchesTabDisplay> tabDisplay = new AtomicReference<>();
doOnJavaFXThread(()->{
tabView.set(new JavafxMatchesTabView());
tabDisplay.set(view.getDisplay());
});
view = tabView.get();
display = tabDisplay.get();
}
如果可行,将此代码移至抽象基础测试可能会更好 class ;-)
嗯,嗯,这比我想象的要简单,而且通常情况下,我只有在提出问题后才能找到解决方案...
无论如何,解决方案是创建一个抽象基础 class 来为您初始化工具包,它就像 "easy" 一样:
@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();
}
}
环境:
- 甲骨文 JDK 1.8u31;
- Intellij IDEA 14.0.3;
- Mockito 1.10.17;
- TestNG 6.8.13.
首先,关于架构的快速目标:JavaFX 称之为 "controller" 我称之为显示,我有一个视图 class 控制显示的元素。
所有这些观点都继承了一个共同的基础class:
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;
}
}
起初我在开始测试时遇到了问题,因为我每次都会得到 "toolkit not initialized"。但是,在问完这个问题后,我
只是我现在偶然发现了一个测试,在这个测试中我又遇到了这个错误:
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
JavafxView.java:24
是这一行:
node = loader.load();
不过,这还不是最奇怪的。
我有另外两个测试 classes 测试另外两个 JavafxView
当我单独 运行 测试 classes 时它们没有问题;只有这个新测试没有...
... 但是如果我 运行 整个 测试套件而不是仅那个测试那么测试成功!
这里是测试的完整源代码:
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();
}
}
那么,呃,我该如何解决这个问题?乍一看,解决方案似乎是我 "port" 自定义 MockMaker
到 TestNG 的 @BeforeClass
... 除了我不明白我怎么能真正这样做:/
嗯,我想我们必须将相同的方法应用于 @BeforeClass
and/or @BeforeMethod
.. 就像这样(未经测试):
private boolean jfxIsSetup;
private void doOnJavaFXThread(Runnable pRun) throws RuntimeException {
if (!jfxIsSetup) {
setupJavaFX();
jfxIsSetup = true;
}
final CountDownLatch countDownLatch = new CountDownLatch(1);
Platform.runLater(() -> {
pRun.run();
countDownLatch.countDown();
});
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
protected void setupJavaFX() throws RuntimeException {
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> {
new JFXPanel(); // initializes JavaFX environment
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@BeforeMethod
public void init() throws IOException
{
AtomicReference<JavafxMatchesTabView> tabView = new AtomicReference<>();
AtomicReference<MatchesTabDisplay> tabDisplay = new AtomicReference<>();
doOnJavaFXThread(()->{
tabView.set(new JavafxMatchesTabView());
tabDisplay.set(view.getDisplay());
});
view = tabView.get();
display = tabDisplay.get();
}
如果可行,将此代码移至抽象基础测试可能会更好 class ;-)
嗯,嗯,这比我想象的要简单,而且通常情况下,我只有在提出问题后才能找到解决方案...
无论如何,解决方案是创建一个抽象基础 class 来为您初始化工具包,它就像 "easy" 一样:
@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();
}
}