Debugging and testing Swing code

Tools and techniques for understanding Swing code that you didn't write

When you need to use or maintain other Java™ developers' code, debugging and testing can help you understand how it works. In the case of visual code though, these powerful practices are more difficult unless you have the appropriate tools. The two open source tools that this article introduces — Swing Explorer and FEST-Swing — can make debugging and testing of Swing UIs simple and reliable. Alex Ruiz shows how to use them to understand a UI's structure, test how it functions, and troubleshoot issues.

Share:

Alex Ruiz (alex.ruiz@oracle.com), Consulting Member of Technical Staff, Oracle

Alex RuizAlex 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.



02 February 2010

Also available in Chinese Russian Japanese

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.

An application to explore

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
A screen shot of the HTML editor application

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.

Introducing Swing Explorer

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
A screen shot of Swing Explorer highlighting the three window views

Swing Explorer provides several views that can help you figure out a Swing GUI's internals:

  1. A tree view displaying the component hierarchy
  2. The GUI under inspection
  3. 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
A screen shot of Swing Explorer showing a 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:

  1. Opening an HTML file
  2. 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.

Introducing FEST-Swing

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 firstName text field and click the "ok" button:
    Listing 1. FEST-Swing's fluent interface
    dialog.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 assertions
    dialog.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:

  1. Selecting the File > Open submenu
  2. Selecting the file to open in the displayed file chooser
  3. 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-Swing Robot, 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 new FrameFixture, capable of simulating user input on a Frame, 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.
  • @RunsInEDT documents that the createNewEditor() method is guaranteed to be executed in the event dispatch thread (EDT).
  • return execute(new GuiQuery<HTMLDocumentEditor>() creates a new instance of HTMLDocumentEditor in 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" JFileChooser launched 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:

  1. Select the Color > Yellow submenu
  2. Type something in the editor
  3. 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.

A more complex test

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
Screen shot of the TableDialogEditDemo window

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:

  1. Scroll the table up or down if necessary to make the row visible.
  2. Click on the cell at row 0, column 2.
  3. Wait for the combo box to appear.
  4. Locate the combo box and click it.
  5. 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 threading

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
Screen shot of the EDT Monitor tab

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
Screen shot of the JUnit HTML report

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:

  1. A GUI component cannot be found. For example, suppose you're looking for a JTextField with the name firstName, 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 thrown ComponentLookupException, making it easier to discover the cause of the failure. In this example, you can inspect the component hierarchy to see if the JTextField was given the right name, or if a JTextField was actually added to the GUI. Listing 7 shows a component hierarchy embedded in a ComponentLookupException:
    Listing 7. ComponentLookupException containing the component hierarchy
    org.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 gave JTextField a wrong name. Instead of firstName, the current name is name.
  2. More than one GUI component is found. This happens if more than one GUI component matches the given search criteria. For example, the name firstName might have been given accidentally to two JTextFields. When looking up a JTextField with name firstName, the lookup fails (and eventually the test, too) because two components have the same name. To help you diagnose the problem, the thrown ComponentLookupException displays all the matching components found, as shown in Listing 8:
    Listing 8. ComponentLookupException containing the list of components that match certain search criteria
    org.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.


Conclusion

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.


Download

DescriptionNameSize
Sample GUI tests for this articlejswingtest-code.zip1.28MB

Resources

Learn

Get products and technologies

  • Swing Explorer: Download Swing Explorer or the Swing Explorer plug-in.
  • FEST: Download FEST-Swing.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=464167
ArticleTitle=Debugging and testing Swing code
publish-date=02022010