Over a million developers have joined DZone.
Platinum Partner

Testing JavaFX UIs: Part 2 of ?

· Java Zone

The Java Zone is brought to you in partnership with ZeroTurnaround. Discover how you can skip the build and redeploy process by using JRebel by ZeroTurnaround.

In order to perform functional testing of a JavaFX UI (as discussed here), we need first to start such UI from a test (written in Java) and then get a reference to the JFrame hosting it. Once we overcome this challenge, we can start simulating user input on the UI under test. Once again, easier said than done.

We already had the class ScriptLauncher that executes a JavaFX script using the Java Scripting API. The following is a simplified version of the original class:

public static void launch(String scriptName) {
final InputStream script = ScriptLauncher.class.getResourceAsStream(scriptName);
execute(new GuiTask() {
protected void executeInEDT() throws Throwable {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByExtension("fx");
InputStreamReader reader = new InputStreamReader(script);

As Michael and I discovered, this approach is very fragile. If the script to launch depends on another one, this launcher will throw an exception. In another words, ScriptLauncher can only execute a JavaFX script that has no dependencies on other scripts. Lame.

My second approach involves launching a compiled JavaFX UI. While debugging ScriptEngine when it launches a JavaFX UI, I discovered that all JavaFX UIs have a static method javafx$run$, which takes a com.sun.javafx.runtime.sequence.Sequence as its only parameter. This method is like the "main" method in regular Java applications, and the ScriptEngine calls it passing TypeInfo.String.emptySequence. I gave it a try and it actually launches the JavaFX UI! Sweet!

Then I discovered that the method javafx$run$ returns a javafx.stage.Stage. I suspected that the JFrame hosting the JavaFX UI has to be there. After a few prinltns, I found a way to get a com.sun.javafx.stage.FrameStageDelegate, which, as you guessed, contains the JFrame I've been looking for. To make the story short, here is the code:

 public static JFrame launch(Class javaFxClass) {
Stage stage = (Stage) staticMethod("javafx$run$").withReturnType(Object.class)
FrameStageDelegate frameDelegate = (FrameStageDelegate) stage.get$impl_stageDelegate().get();
return (JFrame) frameDelegate.get$window().get();

BTW, I'm using FEST-Reflect to call the static method javafx$run$ via Java Reflection.

The code for the new launcher can be found here.

Feedback is always appreciated

From http://www.jroller.com/alexRuiz/

The Java Zone is brought to you in partnership with ZeroTurnaround. Discover how you can skip the build and redeploy process by using JRebel by ZeroTurnaround.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}