Environment:
First, a quick goal about the architecture: what JavaFX calls a "controller" I call a display, and I have a view class which controls the elements of the display.
All such views inherit a common base 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;
}
}
At first I had a problem when I started testing since I would get that "toolkit not initialized" each and every time. However, after asking the question I was provided with a solution which worked pretty well; I could now write tests to test the behavior without a problem...
Except that I now stumble upon a test in which I get this error again:
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
is this line:
node = loader.load();
However, that is not the most strange.
I have two other test classes testing two other JavafxView
which work without a problem when I run the test classes individually; only this new test doesn't...
... But if I run the whole test suite instead of only that test then the test suceeds!
Here is the full source code of the test:
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();
}
}
So, uh, how do I fix that? At a first glance it would seem that the solution could be that I kind of "port" the custom MockMaker
to a TestNG's @BeforeClass
... Except that I don't see how I can really do that :/
Well, uh, it was more simple than I thought, and as is often the case I find the solution only once I've asked the question...
Anyway, the solution is to create an abstract base class which initializes the toolkit for you and it is as "easy" as this:
@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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With