Alex is a Software Engineer working on Android development tools, especially Android Studio, at Google. His interests include Java, API design, OOP, IDEs and testing. Alex spends some of his spare time working on Open Source, blogging, writing technical articles, and speaking at international conferences. The opinions expressed here represent his own and not those of his employer. Alex is a DZone MVB and is not an employee of DZone and has posted 49 posts at DZone. You can read more from them at their website. View Full User Profile

Testing JavaFX UIs: Part 2 of ?

01.01.2009
| 7442 views |
  • submit to reddit
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:

 @RunsInEDT
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);
engine.eval(reader);
}
});
}

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)
.withParameterTypes(Sequence.class)
.in(javaFxClass)
.invoke(TypeInfo.String.emptySequence);
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/

Published at DZone with permission of Alex Ruiz, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)