Testing a Dialog Outside of an Application

476 views
Skip to first unread message

William Speirs

unread,
Feb 11, 2017, 2:34:02 PM2/11/17
to TestFX
I'm looking to test a Dialog, but only the Dialog. The problem I'm having is with setting up the scene. Here's what I have:

    @BeforeClass
    public static void beforeClass() throws Exception {
        FxToolkit.registerPrimaryStage();
        FxToolkit.setupStage((stage) -> {
           mrd = new MatchReplaceDialog(new String[] { "A", "B"});
           stage.setScene(new Scene(new Group(), 500, 500));
        });
        FxToolkit.showStage();
    }

    @AfterClass
    public static void afterClass() throws Exception {
        FxToolkit.hideStage();
    }

    @Test
    public void testShow() {
        mrd.show();
    }

My MatchReplaceDialog does not extend Dialog, but instead has a Dialog instance. Is this bad practice? How do I go about setting this up so I can test the Dialog outside of the rest of the application?

Currently I'm getting:

java.lang.IllegalStateException: Not on FX application thread; currentThread = main
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:229)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.scene.control.Dialog.showAndWait(Dialog.java:325)
at com.es.gui.MatchReplaceDialog.show(MatchReplaceDialog.java:50)
at com.es.gui.MatchReplaceDialogTest.testShow(MatchReplaceDialogTest.java:37)
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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Thoughts?

Benjamin Gudehus

unread,
Feb 11, 2017, 2:43:01 PM2/11/17
to William Speirs, TestFX
Hi William,

>java.lang.IllegalStateException: Not on FX application thread; currentThread = main
>mrd.show();

You need to interact with JavaFX stuff on the JavaFX application thread.

`Plattform.runLater(...)` is normally used for JavaFX. You put the `mrd.show()` as lambda/callback/closure paramater.
TestFX provides `interact(...)` (in `FxRobot`) which also waits for the execution to finish.

>but instead has a Dialog instance. Is this bad practice?

It's fine to use composition (instead of inheritance).

>How do I go about setting this up so I can test the Dialog outside of the rest of the application?

You can use `FxToolkit.setupFixture()` instead of `FxToolkit.setupScene()` if you don't need to setup `Stage`s. But using one of both to setup an external `Dialog` is totally fine.

Hope this helps.

--Benjamin



--
You received this message because you are subscribed to the Google Groups "TestFX" group.
To unsubscribe from this group and stop receiving emails from it, send an email to testfx-discuss+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

William Speirs

unread,
Feb 12, 2017, 12:11:19 PM2/12/17
to TestFX, bill....@gmail.com
On Saturday, February 11, 2017 at 2:43:01 PM UTC-5, Benjamin Gudehus wrote:
Hi William,

You need to interact with JavaFX stuff on the JavaFX application thread.

`Plattform.runLater(...)` is normally used for JavaFX. You put the `mrd.show()` as lambda/callback/closure paramater.

I did this, and I can see the dialog now without an issue... thanks!
 
TestFX provides `interact(...)` (in `FxRobot`) which also waits for the execution to finish.

I assume this is NOT what I want, as I won't be able to interact with a Dialog that is on the screen after calling showAndWait() correct?

>How do I go about setting this up so I can test the Dialog outside of the rest of the application?

You can use `FxToolkit.setupFixture()` instead of `FxToolkit.setupScene()` if you don't need to setup `Stage`s. But using one of both to setup an external `Dialog` is totally fine.

I'm doing this, but now I get a NullPointerException when trying to interact with my Dialog box:

java.lang.NullPointerException
 at org
.testfx.util.NodeQueryUtils.fromWindow(NodeQueryUtils.java:179)
 at java
.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
 at java
.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
 at java
.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
 at java
.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
 at java
.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
 at java
.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
 at java
.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
 at org
.testfx.util.NodeQueryUtils.rootOfWindow(NodeQueryUtils.java:75)
 at org
.testfx.util.NodeQueryUtils.rootsOfWindows(NodeQueryUtils.java:65)
 at org
.testfx.service.finder.impl.NodeFinderImpl.rootsOfWindows(NodeFinderImpl.java:116)
 at org
.testfx.service.finder.impl.NodeFinderImpl.fromAll(NodeFinderImpl.java:77)
 at org
.testfx.service.finder.impl.NodeFinderImpl.lookup(NodeFinderImpl.java:62)
 at org
.testfx.api.FxRobot.lookup(FxRobot.java:212)
 at org
.testfx.api.FxRobot.pointOfVisibleNode(FxRobot.java:1076)
 at org
.testfx.api.FxRobot.clickOn(FxRobot.java:740)
 at org
.testfx.api.FxRobot.rightClickOn(FxRobot.java:799)



This is because it's trying to call window.getScene() and that simply returns null. So maybe FxToolkit.setupFixture() isn't the way to go?

Bill-

William Speirs

unread,
Feb 12, 2017, 12:26:50 PM2/12/17
to TestFX, bill....@gmail.com
One other issue... using Platform.runLater() works, but I have to put in a pause before I can interact with the dialog, otherwise FxRobot tells me that the node is not found. Clearlying I'm missing something here:

        Platform.runLater(() -> mrd.show());

       
Thread.sleep(500);

        fx
.clickOn("#match_field").write("test");

Thanks!

Chris Merrill

unread,
Feb 12, 2017, 1:39:49 PM2/12/17
to TestFX, bill....@gmail.com
You are right - after passing the mrd.show() call off to the display thread, you need to wait for that to execute.

Rather than a sleep, you can use:
  WaitForAsyncUtils.waitForFxEvents();

This will wait for the display thread to become idle. It will usually complete much faster than a safe sleep will take.

William Speirs

unread,
Feb 12, 2017, 5:15:01 PM2/12/17
to TestFX, bill....@gmail.com
On Sunday, February 12, 2017 at 1:39:49 PM UTC-5, Chris Merrill wrote:
Rather than a sleep, you can use:
  WaitForAsyncUtils.waitForFxEvents();

This will wait for the display thread to become idle. It will usually complete much faster than a safe sleep will take.

Thanks, but every once-in-a-while this still causes an issue as the Dialog is not fully rendered. I'm bumping up the attempt count from 5 (default) to 10... seems clunky though.
Reply all
Reply to author
Forward
0 new messages