Automated Test of Swing GUI using Fest

Thursday, August 16, 2007 10:02:34 PM (Romance Standard Time, UTC+01:00)

At my current project I was hired to make some Swing GUI talking to an EJB server. I was new to Swing GUI but I quickly began searching for a test framework fitted for testing them. I came across Fest (Fixtures for Easy Software Testing). Fest is a quite new project, so the API is still subject to some changes (its working towards a “fluent” style), but you can definitely get something out of the current version.

I came from a project where I used Selenium to make automated functional test of web sites, and in fact Fest and Selenium are quite alike in the way test are defined. Its mostly about finding controls like fields and buttons (or links) and then enter text into them or click on them, and off course assert on some attribute of the UI.

When using Selenium you often find yourself setting id attributes on e.g. the links to be able to find them in your test; in Fest you get the same testability of the GUI by setting names on the Swing components. That way you can find them in your test, and avoid basing your test on a specific position in a component hierarchy that are likely to change.


A test in Fest could look like this:

package fest;

import javax.swing.JDialog;
import javax.swing.JTable;

import junit.framework.Assert;
import junit.framework.TestCase;

import org.fest.swing.RobotFixture;
import org.fest.swing.fixture.FrameFixture;

public class FestTest extends TestCase
{
private RobotFixture _robotFixture;
private RecieverFrame _recieverFrame;

private FrameFixture _recieverFrameFixture;
private FrameFixture _senderFrameFixture;

public void setUp()
throws Exception
{
_robotFixture = RobotFixture.robotWithNewAwtHierarchy();
_recieverFrame = new RecieverFrame();
_recieverFrameFixture = new FrameFixture(_robotFixture,
_recieverFrame);
_recieverFrame.pack();

/*
* Make sender frame last, to be able to click on
* buttons as it requires the frame to be in focus
*/
SenderFrame senderFrame = new SenderFrame();
_senderFrameFixture = new FrameFixture(_robotFixture,
senderFrame);
senderFrame.pack();
_robotFixture.showWindow(senderFrame);
}

public void tearDown()
{
_robotFixture.cleanUp();
}

public void testSendMessageToReciever()
throws Exception
{
/*
* Verify that reciever is in a correct initial state
*/
assertEquals("No message", _recieverFrameFixture
.textBox("messageField").text());

_senderFrameFixture.comboBox("forumSelector")
.selectItemWithText("FestForum");
_senderFrameFixture.button("foo").click();

Thread.sleep(500);

_recieverFrameFixture.textBox("messageField")
.requireText("foo");

/*
* Example of finding a Swing component and asserting
* directly on it
*/
JTable messageTable = _robotFixture.finder()
.findByType(JTable.class);
assertEquals("Wrong number of messages", 1,
messageTable.getRowCount());

_senderFrameFixture.button("deleteAllMessages").click();

Thread.sleep(500);

assertEquals("No message", _recieverFrameFixture
.textBox("messageField").text());
assertEquals("Wrong number of messages", 0,
messageTable.getRowCount());

/*
* This is a bit clumsy way to verify that no error
* dialog is present, improvements are possible.
*/
try
{
JDialog errorDialog = _robotFixture.finder()
.findByType(JDialog.class);
Assert
.fail("Expected no dialog present found one '"
+ errorDialog.getTitle() + "'");
}
catch (Exception e)
{
}
}
}

Notice the use of finders to get the components in the Swing GUI, you can choose to find a component by name or class, where the latter makes your test more fragile, I prefer the find-by-name where possible. And it is easy to make your own Matchers to suit your purpose.

A nice feature is that Fest fails if you try to click on a button that is not visible in the frame, verifying that your frame is big enough to contain all your buttons. To improve Fest one could make the assertion failures more informative, they tend to be “Expected false but was true” with little context from the test.

I am using Fest to test multiple Swing frames at a time that communicate together via JMS (Java Messaging) and this can easily be done with Fest. One of the challenges when testing such asynchronous behavior is that your test need to be sleep between invocation and assertion. This is by the way the same challenge you face when testing ajax'ified websites, because your test need to wait until the DOM has been updated by the ajax call.

Fest requires Java5 and can be used in JUnit3 or whatever newer test framework you are using.

Thanks to Alex Ruiz  for putting together Fest.

By Jesper Thaning