The eTextReader Project Source Forge Web Logo

Implementing GUI Tests

Introduction

Much of the functionality in the eTextReader is driven with the Graphical User Interface (GUI). Therefore, testing the functionality of the application involves automatically manipulating the graphical controls of the application, and verifying that the application behaves as expected.

In order to implement such tests, the eTextReader project makes use of the JFCUnit framework, which is an extension of the JUnit testing framework designed explicitly to allow testing of GUIs. The JFCUnit framework provides facilities to find Swing components and to manipulate them with the mouse or keyboard. Since its built on top of the JUnit framework, all of the standard assertion methods are available and can be applied to the Swing components making up the eTextReader application.

Note that we have decided to attach names to each of the GUI components in order to help them be found more readily. This method is more robust than relying on text strings, and will also allow for subsequent internationalization of the software. Names for GUI elements are attached through the use of the ComponentFactory method of creating Swing components. More information on this technique can be found here.

Test Code Location

The tests for the eTextReader application are located in the tests source folder, with the tests associated with the GUI located in the eTextReader.tests.gui package. All subsequent tests should be placed in the tests folder, and classes that perform the GUI tests should be placed into this same package.

An Example test

The classes TestBrowser and TestNavBar provides an example of testing the eTextReader GUI. Before looking at the details of the BrowserTests class, however, we need to look at its base class, BrowserBase, which facilitates some the initial set up of the application.

Details of the BrowserBase class

In all testing cases, it is vital to establish a known state for the application when a test is run. In the JUnit framework, this is done via the protected setUp method. As can be seen by examining the source code in the BrowserBase's setUp method, the environment in which the Browser will run is first configured, and then the default database state is set up. Controlling how this environment is set up and what is stored in the database are controlled by several methods, such as enableAsynchronousLoading and enableTOCLoadOnStartup. These methods can be overridden in the classes making up the actual tests.

Note how constants are set up for the expected bookmark names, default font size, and other values in the BrowserBase class. Setting up these constants make the implementation of tests much easier, and also less subject to fail due to an implementation of a typographical error in the testing code, rather than actual errors in the application code.

After initializing the environment, the main task of the setUp method is to create a new Browser window and make it visible. After this has been accomplished, the manipulation of the Browser in test cases in left to subclasses of BrowserBase.

Several helper methods are also included in the BrowserBase class, including:

While these are not actually called from within the BrowserBase code, they can be used in any test class that extends BrowserBase.

The TestBrowser Class

With that background out of the way, we can move to examining the implementation of the tests in the TestBrowser class. In the TestBrowser setUp method, references to all of the top level Browser menu items are found, using calls to findTopMenu (defined in BrowserBase). A good example of a typical JFCUnit pattern can be seen in the method showFontSizeDialog, whose implementation is as follows:

  1. JMenu changeFontSizeMenu = findSubMenu(preferencesMenu, BROWSER_MENU_CHANGE_FONT_SIZE);
  2. JMenuItem customFontSizeItem = findMenuItem (changeFontSizeMenu, BROWSER_MENU_CUSTOM_FONT_SIZE);
  3. assertNotNull ("Unable to find customFontSize menu item", customFontSizeItem);
  4. getHelper().enterClickAndLeave(new MouseEventData (this, customFontSizeItem));
  5. DialogFinder dialogFinder = new DialogFinder (FontSizeBox.DIALOG_TITLE);
  6. JDialog dialogBox = (JDialog) dialogFinder.find();
  7. assertNotNull ("Pop window not shown when custom font size menu chosen", dialogBox);
  8. return dialogBox

The first two lines find a specific menu item within a submenu of the Preferences menu. Note that in order to find a menu item, a sub-menu and all of its ancestor menus must be showing. Using the findSubMenu and findMenuItem methods from the BrowserBase class ensure that this happens correctly. Next, we are cautious and ensure that the menu item used to set a custom font size was actually found. This is good practice, and ensures that the tests fail with a meaningful error message, rather than a NullPointerException, if a specific GUI component cannot be found.

Line 4 is the standard pattern to click on a non-composite GUI item (such as a JMenuItem or a JButton, but not a JTable, JTabbedPane, etc.). The first parameter to the MouseEventData constructor is the test case associated with the event data, and therefore will almost always be the keyword this. The second parameter indicates the GUI object to be clicked; in this case, it is the custom font size dialog box.

Now let's take a look at the implementation of the testCustomFontSizeOK method, whose job it is to ensure that the appropriate effects occur once a value has been entered into the custom font size dialog and the OK button is clicked. Here's the implementation of this method:

  1. JDialog dialogBox = showFontSizeDialog();
  2. JComboBox sizeBox = findFontSizeComboBox();
  3. assertEquals ("Wrong number of size choices shown in font size combo box", 6, sizeBox.getItemCount());
  4. int newSize = selectLastFontSizeChoice(sizeBox);
  5. JButton okButton = findFontSizeDialogButton(dialogBox, FONTSIZEBOX_BUTTON_OK);
  6. getHelper().enterClickAndLeave(new MouseEventData (this, okButton));
  7. // Now ensure that the value in the database has been changed
  8. DBClient client = new DBClient ();
  9. int size = client.requestFontSize(User.getUser().getUserName());
  10. assertEquals ("Font size value in database not changed to value specified in combo box", newSize, size);

Here we show the custom font size dialog box using the method previously described, and then find a specific component within that dialog box. On line 3 above, the test then verifies that the appropriate number of choices is showing in the font size combo box (note that 6 should probably be defined as a constant somewhere!). Next we select a specific item in the font size combo box, using the method selectLastFontSizeChoice. This method, rather than using GUI events to select a specific item in the combo box, instead uses the combo box's setSelectedIndex method directly. Finally, lines 5 and 6 find the OK button and then click on it.

Lines 8-10 then check to ensure that this set of GUI actions has had the required effects. In this case, we check to see that the current user's font size value has been changed in the database to the value that we specified. It would be appropriate for us to also check that the displayed text's font size has been changed - this has not yet been implemented.