How to test JavaFX Services, the Griffon way - No Fluff Just Stuff

How to test JavaFX Services, the Griffon way

Posted by: Andres Almiray on July 25, 2014

UPDATE: turns out the JUnitParam tests are broken due to Issue #47, however there's an alternative for parameterized tests: Spock. Continue reading at the bottom to see the updated tests

UPDATE 2: JUnitParams 1.0.3 fixes the problem reported by Issue #47.

Today I read a post by Alexander Casall (@sialcasa) about How to test JavaFX Services and thought "maybe I can come up with an alternative using Griffon". So this post is the Griffon version to test out services and controllers in a JavaFX application.

The first thing I'd like to state is that's my conviction that services should be completely separate from UI aspects. In Alexander's example, the LoginService creates an instanced of javafx.concurrent.Task in the body of the login() method. This asserts that the execution always happens inside the UI thread. Hmmm. I agree that updating an observable model property (which appears to be the case for the updateMessage() method) must happen inside the UI thread, however the service could be simpler and just be responsible for plain inputs and outputs.

Here's my (full) version for a LoginService that performs the login operation according to the new design:

In Alexander's example, the service calls out to another component (clientApi) to perform the actual login procedure. This API call may throw an exception if there's a network problem or because of another unforeseen condition. I decided to keep the example simpler and make a trivial username/password check in the service method itself.

Now, because the service is so simple you can test it out in the following manner

A few things of note

  • GriffonUnitRule instantiates a dummy GriffonApplication and performs dependency injection (similarly to a GuiceBerry enabled test).
  • an instance of LoginService is injected to the service field.
  • JUnitParams is used to parameterize the test. John Ferguson Smart (@wakaleo) just posted a great article title "Data-driven unit testing in Java" on this very topic.

Alright, the service is done. But we still have to build a functioning application, isn't it? Somehow, a component must make a call to LoginService and update model properties inside the right thread. This is a job for a controller, like the following one

for those new to Griffon there's a special feature regarding controller actions that you must be aware of, mainly, that they will be executed outside of the UI thread automatically. This behavior is configurable of course. In our case the login() action is executed outside of the UI thread because it relies on the default configuration. This action performs the following tasks

  • disable the login action (of type JavaFXAction). This action is bound to the login button. You can think of JavaFXAction as a similar abstraction as javafx.action.Action. A missing class in the JavaFX 8 API if you ask me.
  • notice that disabling the action happens inside the UI thread. The code makes use of the lambda expression syntax (which is nice!).
  • invoke the loginService.login() with values coming from the model. How those values are placed in the model is of no concern to the controller, but we can assume they come from bindings made to UI components.
  • update another model property with the output of the login procedure. This update happens inside the UI thread too because we assume the property is bound to an UI component.
  • finally re-enabled the login action.

Testing this controller will require that we're able to handle asynchronous validations, precisely the problem that Alexander addresses in his post. But instead of using a custom JUnitRunner and CompletableFuture we'll leverage Griffon's test support coupled with an amazing testing utility called Awaitility. Here's how the test looks

This is another parameterized test using JUnitParams, another reason why we can't use a custom JUnitRunner like the one shown by Alexander. Instead we go with the simplest route in order to initialize the JavaFX toolkit: create an instance of javafx.embed.swing.JFXPanel during class initialization. Waiting for a condition to be reached in an asynchronous way is quite trivial with Awaitility, as seen in line 48. I'd say the code is quite readable, woudln't you agree? The IsBlank matchers is not provided by the Hamcrest, but it's simple to create

In order to be able to use both JunitParams and Awaitility we must define dependencies in the build file. Griffon uses Gradle by default (although you can use Maven too), so the build.gradle file contains the following dependency entries

You may be wondering about the rest of the code, Model and View. Here's the model that shows usage of plain JavaFX properties

Definitely verbose. Here's hoping a Lombok extension is built in the future that can create such boiler-plate code for JavaFX properties. Next is the view, where we see the linking between model properties and UI components

The important bits happen inside the init() method. This is where model properties are bound to their respective UI components. Controller actions are also connected to their intended targets. Notice that the view uses the @FXML annotations to mark the injection points for UI components. This means it's SampleView and not the SampleController the one that receives the injections. This switch is automatically handled by the loadFromFXML() method. Finally we see the FXML loaded by the view

This is a run-of-the-mill, SceneBuilder generated FXML file. The only thing that make look suspicious is the button's id. Griffon follows a naming convention to be able to connect controller actions to control targets. The following screenshots show the application as it appears after some login attempts

no login blank entries
successful login invalid entries

And there you have it. And alternate way to write and test controller and services in JavaFX.

Keep on Groovying!

UPDATE: So JUnitParams has trouble due to Issue #47. Fortunately there's an alternative for parameterizing tests in the form of Spock. We can uses Spock's data driven feature methods to parameterize the tests for both LoginService and SampleController. I also took the liberty of updating LoginService to use a delegate ClientApi component to perform the actual login procedure. Here's how the LoginServiceSpec looks like

Notice the usage of Spock's interactions. They're like regular mocks, but groovier ;-) This also shows one of the many ways to override a JSR-330 biding in a testcase, in this case the specification requires a mock of the ClientApi type.

Next is the SampleControllerSpec, containing an almost identical setup as SampleControllerTest, the main difference being how the parameterization is setup.

So there you go. If you're willing to branch out and try out other testing tools, such as Spock, your testing experience should be a more gratifying one.

Andres Almiray

About Andres Almiray

Andres is a Java/Groovy developer and a Java Champion with more than 20 years of experience in software design and development. He has been involved in web and desktop application development since the early days of Java. Andres is a true believer in open source and has participated on popular projects like Groovy, Griffon, and DbUnit, as well as starting his own projects (Json-lib, EZMorph, GraphicsBuilder, JideBuilder). Founding member of the Griffon framework and Hackergarten community event. https://ch.linkedin.com/in/aalmiray

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »