Swing is one of the more powerful GUI toolkits available; it's extensible, configurable, and cross-platform. But Swing's flexibility is both its major strength and a great weakness. With Swing, you can construct the same UI in many different ways. For example, you can use insets, empty borders, or fillers to put space between GUI components. Given Swing's extensive stock of options, understanding an existing GUI can be as daunting a task as writing a new one, and mapping its visual appearance to the underlying code is far from trivial. (Try visualizing a GUI while reading a few lines of code that uses GridBagLayout.)
Whether you're maintaining Swing GUIs you didn't write or integrating third-party GUI components into your applications, a sensible approach to understand the code is to write tests. You explore the internals of the unknown code while writing the tests. As a valuable side effect, you end up with a test suite that can help prevent the introduction of regressions when you maintain the code. In the case of third-party GUI components, the test suite helps you find out if new a version of the library has introduced any behavioral changes.
A good start is to write functional tests to understand how the GUI behaves in response to user input. Writing tests for GUIs is more complex than writing tests for non-visual code, because:
- Ideally, tests must be automated, but GUIs are designed for humans — not computer programs — to use.
- Conventional unit testing, involving tests of isolated classes, is unsuitable for GUI components. In GUI terms, a "unit" involves cooperation of more than one GUI component, which can itself consist of more than one class.
- GUIs respond to user-generated events. To test GUIs, you need a way to simulate user input, wait until the generated events have been broadcast to all listeners, and then check the result as the GUI would appear to the user. Writing code that simulates user interaction with GUIs can be tedious and error-prone.
- Changes in the GUI's layout should not affect robust functional tests.
An additional issue is that you must already know the structure and behavior of the GUI you want to test, otherwise you don't know which components the automated test should use and what needs to be verified. In general, to write a GUI test you must know:
- The components that are present in the GUI to test
- How you can uniquely identify such components in your tests
- The expected state (or properties) of the components in a particular use case
You might be able to figure out a GUI's structure by using a visual design tool (such as NetBeans Matisse). But this type of tool shows only a GUI's design-time information, which can be different from what you see at run time. For example, some components may be made visible or invisible in response to user input.
Traditional debuggers cannot help you learn the state of a GUI while it's executing a particular use case. When the debugger stops at a breakpoint placed in Swing code, GUI painting is interrupted resulting in GUI that looks like a blank square. Ideally, you want to see how the GUI behaves while you step through it with a debugger.
Fortunately, two open source tools — Swing Explorer and FEST-Swing — can help you get up to speed quickly when you need to understand existing Swing code. This article introduces you to these tools, showing you how to use them in combination to inspect an application's GUI structure, test its functionality, and identify potential problems.
For most of the article's examples, I'll use a free, functional HTML editor called HTMLDocumentEditor as an application to test (see Resources). If you want to work through the examples yourself, you can download the application and the sample test code. Figure 1 shows HTMLDocumentEditor in action:
Figure 1. The HTML editor
Before writing GUI tests, you need to understand how the GUI is composed. The HTML editor is quite simple, consisting of a text area and several menus for opening, saving, and editing HTML documents.
It is also important to know the specific type of each component. This will help you know
which actions or properties a GUI component provides through its API for you to use in
your tests. For the HTML editor, you need to determine if the text area is a JTextArea, JTextPane, or a custom GUI
component. One way to determine GUI component types is by inspecting the source code.
Depending on how the GUI has been implemented, this can be an easy or a challenging task. HTMLDocumentEditor's source code is readable and easy to grasp, and a quick inspection of it reveals that the text area is a JTextPane. But at some point in your career, you're likely to encounter poorly written GUI code that is hard to understand. When this happens, you can either spend a considerable amount of time deciphering that code or find a tool to do it for you efficiently.
Swing Explorer allows you to inspect the internals of Swing GUIs visually (see Resources). Its simple, intuitive UI makes it easy to discover all the components in a GUI, investigate how they are painted, inspect their properties at any point in time, and much more.
Swing Explorer is distributed as both a stand-alone application and as plug-ins for Eclipse and NetBeans. The recommended way to use it is through the IDE plug-ins. In this article, I'll use the Eclipse plug-in (see Resources).
After installing the plug-in, I launch the HTML editor main class using Swing Explorer, as shown in Figure 2:
Figure 2. Editor application launched in Swing Explorer
Swing Explorer provides several views that can help you figure out a Swing GUI's internals:
- A tree view displaying the component hierarchy
- The GUI under inspection
- A tabbed pane that shows properties of a selected component (name, size, and so on) and includes other useful, interesting tools (see Resources for details)
Figuring out how a GUI is structured is easy with Swing Explorer. For the purpose of
this exercise, assume that you can't find out by reading the source code the type of
component used as the text area in the HTML editor. With Swing Explorer, you just
either select the text area on the component tree view or click the component itself
in the displayed GUI. In Figure 3 below, Swing Explorer confirms that the text area is a JTextPane:
Figure 3. Swing Explorer showing properties of selected component
Learning and testing application behavior
Once the structure of the GUI you want to test is clear, the next step is to learn the application's behavior so that you can know which expectations you want to verify. You can do this in different ways: by interviewing the current end users, reading the application's documentation (if any), or simply using the application yourself.
As a starting point, I'll choose two use cases to test:
- Opening an HTML file
- Changing the color of a document's font
Now I'm ready to start writing functional GUI tests.
Functional GUI testing verifies that an application works as expected. It focuses on the application's behavior rather than the GUI's appearance. The following factors are essential to creating a robust functional GUI test:
- Being able to simulate user input (keyboard and mouse)
- Having a reliable mechanism for finding GUI components
- Being able to tolerate changes in a component's position or layout
A nonstarter: Using Robot directly
To ensure that an automated test truly simulates user input, you need to generate "native"
events at the operating-system level, just as if a user were using the keyboard and mouse.
The JDK has provided support for input simulation since version 1.3 through the Abstract
Window Toolkit (AWT) Robot. But the Robot works with screen coordinates instead of Swing component
references, so using it directly in tests will make the tests fragile, meaning any change in layout will break them.
And the AWT Robot is too low-level; it only knows how to click
mouse buttons and press keys. You'd need to write the code for translating high-level
actions such as select the third element in this combo box into Robot actions. That could take a lot of work, depending on the number of actions needed in your tests and the different types of components involved.
Furthermore, the AWT Robot doesn't provide a reliable mechanism
for component lookup (such as find button with text "OK"). Again, you'd need to write your own.
In general, working directly with the AWT Robot requires considerable energy and time. When writing functional GUI tests, you need to concentrate on the behavior you want to verify, not on the underlying plumbing that makes GUI testing possible.
The FEST (Fixtures for Easy Software Testing) Swing module is a library that makes it easy to create and maintain robust functional GUI tests. Its main features include:
- Built on top of the AWT
Robot, to simulate true user input. -
A compact, intuitive, and readable fluent interface that simplifies creation and maintenance of functional GUI tests. Listing 1 shows how to code the high-level actions enter the text "luke" in the
firstNametext field and click the "ok" button:
Listing 1. FEST-Swing's fluent interfacedialog.textBox("firstName").enterText("Luke"); dialog.button("ok").click();
-
Assertion methods that verify the state of GUI components. Listing 2 shows an assertion that verifies that the text of the label with name "answer" is "21":
Listing 2. FEST-Swing's assertionsdialog.label("answer").requireText("21");
- Promotes robust tests: layout changes do not break tests.
- Supports Swing components present in the JDK.
- Supports JUnit 4 and TestNG.
- Provides verification of correct Swing threading use.
- Makes troubleshooting failing tests easier.
Writing functional GUI tests with FEST-Swing
Now that I understand the structure of the editor application's GUI, have collected the use cases to test, and have found a reliable testing tool, I'm ready to write a functional GUI test.
Use case: Opening an HTML file
Opening a file in the HTML editor involves:
- Selecting the File > Open submenu
- Selecting the file to open in the displayed file chooser
- Verifying that the editor loaded the contents of the file
Listing 3 shows the code for this use case:
Listing 3. Testing opening an HTML file
public class HTMLDocumentEditor_Test extends FestSwingJUnitTestCase {
private FrameFixture editor;
protected void onSetUp() {
editor = new FrameFixture(robot(), createNewEditor());
editor.show();
}
@RunsInEDT
private static HTMLDocumentEditor createNewEditor() {
return execute(new GuiQuery<HTMLDocumentEditor>() {
protected HTMLDocumentEditor executeInEDT() {
return new HTMLDocumentEditor();
}
});
}
@Test
public void should_open_file() {
editor.menuItemWithPath("File", "Open").click();
JFileChooserFixture fileChooser = findFileChooser().using(robot());
fileChooser.setCurrentDirectory(temporaryFolder())
.selectFile(new File("helloworld.html"))
.approve();
assertThat(editor.textBox("document").text()).contains("Hello");
}
}
|
In detail, here's what the test in Listing 3 is doing:
- The first line extends FEST-Swing's
FestSwingJUnitTestCase. It provides automatic creation of the FEST-SwingRobot, verification of correct Swing threading (more on that later), and automatic cleanup of resources (disposes open windows, releases mouse and keyboard, and so on). editor = new FrameFixture(robot(), createNewEditor());creates a newFrameFixture, capable of simulating user input on aFrame, look up components inside of it (using a variety of search criteria), and verify its state.editor.show();displays the HTML editor on the screen.@RunsInEDTdocuments that thecreateNewEditor()method is guaranteed to be executed in the event dispatch thread (EDT).return execute(new GuiQuery<HTMLDocumentEditor>()creates a new instance ofHTMLDocumentEditorin the EDT.- In
editor.menuItemWithPath("File", "Open").click();, FEST-Swing simulates a user clicking the File > Open submenu. - In
JFileChooserFixture fileChooser = findFileChooser().using(robot());, FEST-Swing finds the "Open File"JFileChooserlaunched by the HTML editor. - In the next three lines, FEST-Swing simulates a user selecting the helloworld.html file located in the system's temporary folder.
assertThat(editor.textBox("document").text()).contains("Hello");verifies that the file has been loaded in the editor by checking that it contains the word Hello.
Note that Listing 3 looks up the JTextPane by name (editor). This is the most reliable way to find components in a test; it guarantees that component lookup will never fail, even if the GUI's layout changes in the future.
Use case: Changing the color of a document's font
To verify that the HTML editor changes the color of a document's font to yellow, you need to:
- Select the Color > Yellow submenu
- Type something in the editor
- Verify that the color of the entered text is yellow
Listing 4 shows how to do this using FEST-Swing:
Listing 4. Test for changing the document's font color
@Test
public void should_change_document_color() {
editor.menuItemWithPath("Color", "Yellow").click();
JTextComponentFixture textBox = editor.textBox();
textBox.enterText("Hello");
assertThat(textBox.text()).contains("<font color=\"#ffff00\">Hello</font>");
}
|
So far I've shown how to test simple GUI components such as menus and text fields. Next I'll cover a less straightforward testing scenario.
To demonstrate FEST-Swing's intuitive and compact API, I'll use one of Swing's highly complex components — a JTable.
I'll use the TableDialogEditoDemo application from Sun's Swing Tutorial (see Resources). The application uses a JTable with custom editors: JComboBoxes and JCheckBoxes, as shown in Figure 4:
Figure 4.
TableDialogEditDemo
As an example, I'll write a test that simulates a user selecting the second element in the combo box at row 0. The actions for the test to perform are:
- Scroll the table up or down if necessary to make the row visible.
- Click on the cell at row 0, column 2.
- Wait for the combo box to appear.
- Locate the combo box and click it.
- Select the second element from the combo box.
That is only the high-level description of the actions I need to code. Writing the actual code is not a trivial task. Fortunately, FEST-Swing's API makes this task pretty simple, as shown in Listing 5:
Listing 5. Selecting the third element in the combo box at row 0
dialog.table.enterValue(row(0).column(2), "Knitting"); |
FEST-Swing can simplify writing and reading GUI tests, even complex ones.
Swing is a single-threaded UI toolkit. Because it is not thread-safe, all Swing code must be executed in the EDT. As the official documentation states, invoking Swing code from multiple threads risks thread interference or memory-consistency errors (see Resources).
Swing's threading policy states:
- Swing components must be created in the EDT.
- Swing components must be accessed in the EDT, unless you call methods documented as thread-safe.
Although they sound simple, it is too easy to break these rules. Swing does not provide any run-time checks for correct EDT usage and, most of the time, you can get away with an apparently "well behaved" Swing UI that breaks these rules.
Both Swing Explorer and FEST-Swing provide support for finding violations of Swing's threading policy. Figure 5 shows Swing Explorer's EDT monitor. The EDT monitor can report EDT access violations while the application is being executed.
Figure 5. Swing Explorer's EDT monitor
FEST-Swing provides FailOnThreadViolationRepaintManager to check for EDT access violations, forcing a test to fail if any is detected. Configuration is easy: place it in the set-up method marked with the @BeforeClass annotation, as shown in Listing 6:
Listing 6. Installing
FailOnThreadViolationRepaintManager
@BeforeClass public void setUpOnce() {
FailOnThreadViolationRepaintManager.install();
}
|
Alternatively, UI tests can subclass FEST-Swing's FestSwingTestngTestCase or FestSwingJunitTestCase, which already install FailOnThreadViolationRepaintManager. FEST-Swing also provides useful abstractions that ensure that access to Swing components is done in the EDT. For more information, see Resources.
Troubleshooting failing GUI tests
Regardless of the library used for writing functional GUI tests, this type of test is vulnerable to environment-related events. FEST-Swing is no exception. For example, a scheduled anti-virus scan may pop up a dialog blocking the GUI you are testing. The FEST-Swing Robot will not be able to access the GUI and will time out eventually, forcing the test to fail. The cause of the failure is not a programming error; it is just bad timing.
A useful feature of FEST-Swing is its ability to take a screen shot of the desktop at the moment of a test failure. This screen shot can be automatically embedded in a JUnit or TestNG report, or saved in a directory when you're executing a single test from within an IDE. Figure 6 shows a JUnit HTML report with a failing GUI test. Note the link FEST-Swing added to the screen shot of the desktop, taken at the moment of the test failure.
Figure 6. JUnit HTML report linking from failed test to desktop screen shot
Another typical cause for test failures is component-lookup failures. The recommended way to look up components is by their unique names. Sometimes the GUI you need to test does not have unique names for its components, forcing you to use custom search criteria. There are two types of component-lookup failures:
-
A GUI component cannot be found. For example, suppose you're looking for a
JTextFieldwith the namefirstName, but the original developer forgot to assign that name to the component. In cases like this, FEST-Swing includes the available component hierarchy in the thrownComponentLookupException, making it easier to discover the cause of the failure. In this example, you can inspect the component hierarchy to see if theJTextFieldwas given the right name, or if aJTextFieldwas actually added to the GUI. Listing 7 shows a component hierarchy embedded in aComponentLookupException:
Listing 7.ComponentLookupExceptioncontaining the component hierarchyorg.fest.swing.exception.ComponentLookupException: Unable to find component using matcher org.fest.swing.core.NameMatcher[name='ok', requireShowing=false]. Component hierarchy: myapp.MyFrame[name='testFrame', title='Test', enabled=true, showing=true] javax.swing.JRootPane[] javax.swing.JPanel[name='null.glassPane'] javax.swing.JLayeredPane[] javax.swing.JPanel[name='null.contentPane'] javax.swing.JTextField[name='name', text='Click Me', enabled=true]
The component hierarchy in Listing 7 helps you conclude that the original developer gaveJTextFielda wrong name. Instead offirstName, the current name isname. -
More than one GUI component is found. This happens if more than one GUI component
matches the given search criteria. For example, the name
firstNamemight have been given accidentally to twoJTextFields. When looking up aJTextFieldwith namefirstName, the lookup fails (and eventually the test, too) because two components have the same name. To help you diagnose the problem, the thrownComponentLookupExceptiondisplays all the matching components found, as shown in Listing 8:
Listing 8.ComponentLookupExceptioncontaining the list of components that match certain search criteriaorg.fest.swing.exception.ComponentLookupException: Found more than one component using matcher org.fest.swing.core.NameMatcher[ name='firstName', type=javax.swing.JTextField, requireShowing=false]. Found: javax.swing.JTextField[name='firstName', text='', enabled=true] javax.swing.JTextField[name='firstName', text='', enabled=true]
Sometimes, it can be challenging to inspect the component hierarchy in a thrown ComponentLookupException, especially when you're dealing with GUIs
that have a vast number of components. Once again, Swing Explorer can be a great aid.
As was shown, you can select and inspect the properties of any component in the component hierarchy by just clicking the component directly. Big component hierarchies are a lot easier to digest in Swing Explorer's GUI than the text-based representation provided by ComponentLookupException.
Swing's power comes at the price of complexity; understanding Swing code can be as challenging as writing it. Writing tests to explore unknown GUI code is more complex than writing tests for non-visual code. Fortunately, Swing Explorer and FEST-Swing can help take the tedium and guesswork out this process. With Swing Explorer, you can explore the structure of a GUI while the application is running. Once you understand the structure and behavior of the GUI to test, you can use FEST-Swing's compact and intuitive API to write functional GUI tests. Besides its fluent API, FEST-Swing provides verification of correct use of Swing threading and features that can help you save time when you're troubleshooting failed GUI tests. This article has merely scratched the surface of what with this powerful duo lets you accomplish.
As useful as Swing Explorer and FEST-Swing are, an even better solution would be a recording/playback tool that records user interactions in Java code, as if it were created manually by a developer. Record/playback tools give you a test suite in the shortest possible time. You interact with an existing GUI, and all the user generated-events are recorded in a script. You can later replay the script to re-create user interactions for a particular scenario. The major weakness of existing record/playback tools is expensive maintenance of the generated tests. Any change in the application requires re-recording all test scenarios. At the same time, recording all test scenarios can create duplicate test code when you're testing similar scenarios. Recorded scripts are often long and written in proprietary languages lacking object-oriented (OO) language features. Modularization of repetitive actions is labor-intensive and error-prone and typically requires learning a new programming language.
By using a record/playback tool based on a popular and mature OO language — the Java language — developers could benefit from using feature-rich IDEs that improve productivity and lower maintenance costs by making boring, error-prone tasks (such as refactoring) quick and trivial. This is exactly what the FEST project team is currently working on: a playback/recording tool that generates clean object-oriented GUI tests using the FEST-Swing Java API. We expect to have a preview of this tool available by 2Q2010.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample GUI tests for this article | jswingtest-code.zip | 1.28MB | HTTP |
Information about download methods
Learn
-
Swing Explorer: Visit the Swing Explorer project site.
-
FEST-Swing: Find links to documentation, articles, and software downloads.
-
"Writing EDT-safe Swing UI tests" (Alex Ruiz's Blog, July 2009): Learn the steps for writing test cases that respect Swing threading policies.
-
"In pursuit of code quality: Automate GUI testing with TestNG-Abbot" (Andrew Glover, developerWorks, February 2007): Read about GUI testing with TestNG-Abbot, the predecessor of FEST-Swing.
-
"WYSIWYG Html Editor with JEditorPane and HTMLEditorKit" (Artima.com, February 2002): Charles Bell posted the source for HTMLDocumentEditor here.
-
How to Use Tables: This is the Swing tutorial on tables.
-
Browse the
technology bookstore for books on these and other technical topics.
-
developerWorks Java technology zone: Find hundreds of articles about every aspect of Java programming.
Get products and technologies
-
Swing Explorer: Download Swing Explorer or the Swing Explorer plug-in.
-
FEST: Download FEST-Swing.
Discuss
-
Swing Explorer mailing lists: Subscribe or browse the archives.
-
FEST Users' Group: Join the FEST mailing list.
- Get involved in the My developerWorks community.

Alex Ruiz enjoys reading anything related to Java development, testing, object-oriented programming, aspect-oriented programming, and concurrency. Programming is his first love. Alex is the creator of FEST, an innovative Java library that aims at making Swing and JavaFX testing, and testing in general, easier. He works as software engineer in the development tools organization at Oracle. Before joining Oracle, Alex was a consultant for ThoughtWorks. Check out Alex's blog.



