Headless Testing with TestFX

Jérome Cambon wrote a nice introduction to TestFX and how to enable the headless mode using Monocle.

I tried to reproduce the steps he described for running my TestFX GUI tests in headless mode from Eclipse so I can keep using my mouse while they are running. The crux is setting the VM arguments in Eclipse. First, I tried to set

in the VM arguments section of the JRE tab in my Maven run configuration:

Maven run configuration in Eclipse

This did not work. I could still see the TextFX robot move the mouse cursor on my screen.

I had to specify the VM arguments using Maven’s ‘argLine’ parameter:

Maven argLine parameter

If anyone knows why setting the VM arguments in the run configuration did not work, please let know.

A Potential Pitfall with the TextFormatter Class in JavaFX

A common use case for the TextFormatter class in JavaFX is a text field that should convert any lower-case character input into an upper-case character. This can be achieved easily by defining a filter for the text formatter as described here. A straightforward implementation could look like this:

This implementation comes with one pitfall, though. When the (German) user types the character “ß” into the text field, it will be converted into “SS” because that’s how Germans roll. So the resulting string is one character longer than the input string. Why is this a problem? Well, the cursor position will be between the two “S” characters after the conversion:

cursor_ss

When the user types in the next character, it will be inserted between the two “S” characters, not behind them. Bummer.

Luckily, the masterminds behind JavaFX give us the tools to correct this unfortunate behaviour since the change object that the filter works on allows for setting the anchor and caret position in the text field. We need to correct them by the difference between the string lengths before and after the conversion:

This puts the cursor at the end of the string no matter how many “ß” characters the user types in or pastes into the text field.

The TextFormatter Class in JavaFX: How to Restrict User Input in a Text Field

There are a lot of code examples for restricting or modifying user input into a JavaFX text field. Most examples I have seen suggest adding a change listener to the text field’s text property. Here’s how you would allow only lower-case characters in your text field using the change listener approach:

This approach comes with one drawback: you’ll have two events being triggered by two changes of the text property. The first change is caused by the direct user input, the second change is caused by the manipulation of the user input by the change listener. The user won’t notice these two events, but somewhere in your code you have another listener for the text property, that listener will receive two events, one with the “invalid” change and a second one with the “valid” change.

Another approach was suggested by Richard Bair a long time ago. His suggestion resulted in the TextFormatter  class being added to JavaFX with version 8u40. It’s a clean way to format, filter, or restrict user input. Here’s how it works:

What’s still missing is support for the backspace key to delete a character in the text field, but that’s just my silly example implementation. [Update 2016-12-28: The implementation was improved to support the deletion and selection of characters.]

This approach intercepts the user input before it’s written into the text property and thus fires only one event. Before that happens, you can examine and modify the Change  object in the UnaryOperator  defined in getFilter()  method.

In addition to filtering, a TextFormatter  object can convert a value to a string representation and vice versa. From the Javadoc:

A value converter and value  can be used to provide a special format that represents a value of type V. If the control is editable and the text is changed by the user, the value is then updated to correspond to the text.

Sounds pretty handy.

Adding JavaFX Properties to a DTO

Now that I have understood what a DTO is good for, I’m thinking about ways to improve it. As they are right now, our DTOs are POJOs extended by JSR 303 annotations for defining constraints on their fields/methods. The annotations are duplicates of the annotations defined on the corresponding entities.

Since I have worked with the UI code most of my time so far, I have felt the drawback of not having JavaFX properties in our DTOs: they cannot be observed easily by the GUI controls. There is a wrapping mechanism to add this observability, but it is rather complicated to use and requires a lot of extra code.

So why not make the DTOs full JavaFX beans? After all, we would not be the first project to do this. Two common objections were raised when I brought this up:

  1. DTOs are created on the server which should not have dependencies to a GUI/client technology.
  2. We would need to mix JavaFX beans with JSR 303 annotations.

Let’s address the first point. Yes, having a GUI dependency in your server code is not a good idea. But are JavaFX properties a GUI technology? Or even a client technology? It’s a bit unfortunate that they were introduced as part of JavaFX in a javafx.* package such that everyone identifies them as a GUI technology at first sight. It’s an image problem. I would argue they are a self-contained technology which is independent of any GUI parts of JavaFX and could therefore be used for any data model, no matter if it’s on the client, on the server, or shared between both. Even if one does not use JavaFX for the frontend, depending on JavaFX properties would do almost no harm in practice since JavaFX is part of the JDK and your server code certainly depends on the JDK. So the no client dependencies on the server argument is merely a formal one in my opinion.

What about the second point, mixing JavaFX properties with bean validation annotations? I have never tried it myself, but I have the feeling it cannot be that big of a problem. If it works in the FXForm2 demo, why should there be a fundamental incompatibility between these two technologies? But I admit that a good prototype is needed to make a final judgment.

We will have a meeting on this tomorrow. Let’s see what the others think.

 

Can There Be Too Much of a Good Thing?

I recently worked on JavaFX UI code that was new to me. In order to extend the existing UI by a simple label, I needed access to a bean of the underlying data model. Much to my surprise, I had to dig deep to find it hidden under multiple layers of wrappers. This was the data structure:

A bean wrapped in a BeanWrapper wrapped in an ObjectProperty wrapped in a WrapperPropertyProxy. Quite impressive.

Wow. Clearly, someone tried to satisfy their fetish for wrappers and indirection. I’m going show this image to the next computer science student I come across and ask them how many design (anti-)patterns they can see.

Headless UI Testing with TestFX and JavaFX 8

JavaFX is a great UI toolkit. TestFX is a great library for testing the user interfaces written in JavaFX. Writing graphical tests with TestFX is simple and fast, but one challenge remains when you build your software using a headless build machine: how can you perform your UI tests in headless mode during your build?

Luckily, JavaFX 8 Update 20 will contain Monocle, which should allow us to run these headless tests.

First analysis using the latest beta of JDK 8u20 indicates that these tests are feasible, but require some changes in the TestFX library. We used a simple standalone JavaFX application for which we wrote a basic TestFX test class. We ran this test using the following command line options:

Unfortunately, this was not enough to make the test work. So we took a deeper look at the inner workings of TestFX and found that a working solution required the following changes:

  1. refactor a part of the TestFX code such that different implementations of the org.loadui.testfx.framework.robot.ScreenRobot interface can be used. This allowed us to make test use an other robot implementation, in this case an adapter for MonocleRobot. The current TestFX implementation relies on java.awt.Robot, which uses the system event queue to move the system cursor. This, of course, does not integrate with Monocle in headless mode.
  2. implement a ScreenRobot adapter for MonocleRobot. MonocleRobot is in internal package and thus not available via public API. In order to obtain an instance of this class, we had to call:

This setup gave us a green test eventually. 🙂