No, those are not ingredients for a new fruit salad recipe. These are just the components I used in one of my pet project: it’ss a Swing application in which I wanted to try out CDI. I ended up with Weld SE, which is the CDI RI from JBoss.
The application was tested alright with TestNG (regular users know about my preference of TestNG over JUnit) save the Swing GUI. A little browsing on the Net convinced me the FEST Swing testing framework was the right solution:
- It offers a DSL for end-to-end functional testing from GUI.
- It has an utility class that checks that Swing components methods are called on the Event Dispatch Thread (EDT).
- It may check calls to
System.exit()
. - It has a bunch of verify methods such as
requireEnabled()
,requireVisible()
,requireValue()
and many others that depend on the component’s type.
The challenge was to make TestNG, FEST and CDI work together.
Luckily, FEST already integrates TestNG in the form of the FestSwingTestngTestCase
class.
This utility class checks for point 2 above (EDT use rule) and create a "robot" that can simulates events on the GUI.
Fixture
FEST manages GUI interaction through fixtures, wrapper around components that can pilot tests. So, just declare your fixture as a test class attribute that will be set in the setup sequence.
Launch Weld in tests
FEST offers an initialization hook in the form of the onSetup()
method, called by FestSwingTestngTestCase
.
In order to launch Weld at test setup, use the following implementation:
protected void onSetUp() {
// container is of type WeldContainer
// it should be declared as a class attribute in order to be cleanly shutdow in the tear down step
container = new Weld().initialize();
MainFrame frame = GuiActionRunner.execute(new GuiQuery<MainFrame>() {
@Override
protected MainFrame executeInEDT() throws Throwable {
return container.instance().select(MainFrame.class).get();
}
});
// window is a test class attribute
window = new FrameFixture(robot(), frame);
window.show();
}
This will display the window fixture that will wrap the application’s main window.
Generate screenshots on failure
For GUI testing, test failure messages are not enough. Fortunately, FEST let us generate screenshots when a test fails. Just annotate the test class:
@GUITest
@Listeners(org.fest.swing.testng.listener.ScreenshotOnFailureListener.class)
public abstract class MainFrameTestCase extends FestSwingTestngTestCase {
...
}
Best practices
Clicking a button on the frame fixture is just a matter of calling the click()
method on the fixture, passing the button label as a parameter.
During developement, however, I realized it would be better to create a method for each button so that it’s easier for developers to read tests.
Expanding this best practice can lead to functional-like testing:
selectCivility(Civility.MISTER);
enterFirstName("Nicolas");
enterLastName("Frankel");
Conclusion
I was very wary at first of testing the CDI-wired GUI. I thought it would be hard and would be too time-consuming given the expected benefits, I was wrong. Uniting TestNG and CDI is a breeze thanks to FEST. Having written a bunch of tests, I uncovered some nasty bugs. Life is good!