Abbot Topics
Abbot framework for testing Java GUI components and programs
All materials Copyright © 2002-2011 All Rights Reserved Timothy Wall

This version 1.2.0-RC2

Tutorial 2: Testing A Group of Components (Part I)

This is a short example of how to write tests against a composite component prior to writing it. This example is written in the context of JUnit, and assume you're familiar with that framework. If not, read the cookbook first.

How to write a simple test fixture

In this example, we want to write a component which displays the currently selected item from list in a label below that list. We first write a test fixture that displays our LabeledList (the main routine automatically runs the test suite for this fixture, or runs a single test if one is specified on the command line). Explicitly disposing of the frame is not necessary, since the ComponentTestFixture will dispose of any frames after tests finish running.


import java.awt.*;
import javax.swing.*;
import junit.framework.*;
import junit.extensions.abbot.*;

import abbot.script.ComponentReference;
import abbot.tester.ComponentTester;

public class LabeledListTest extends ComponentTestFixture {

    public void testLabelChangedOnSelectionChange() throws Throwable {
        String[] contents = { "one", "two", "three" };
        final LabeledList labeledList = new LabeledList(contents);
        Frame frame = showFrame(labeledList);
        // ...
    }

    public LabeledListTest(String name) { super(name); }

    public static void main(String[] args) {
        TestHelper.runTests(args, LabeledListTest.class);
    }
}
This won't compile until we get a LabeledList to test against, so let's do that.

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;

public class LabeledList extends JPanel {
    private JList list;
    private JLabel label;
    public LabeledList(String[] initialContents) {
        setLayout(new BorderLayout());
        list = new JList(initialContents);
        add(list, BorderLayout.CENTER);
        label = new JLabel("Selected: ");
        add(label, BorderLayout.SOUTH);
    }
}
Now we go back and add to the test. We want to select a list item and ensure that the label reflects the selected item. So we first look up the list. Note that while we could make the LabeledList member public or otherwise directly accessible, that's making private data visible solely for the sake of a test, and ends up cluttering the interface. Let the Abbot framework do the work instead...

    import abbot.finder.matchers.*;

    ...

    public void testLabelChangeOnSelectionChange() throws Exception {
        // ...
        // The interface abbot.finder.Matcher allows you to define whatever
        // matching specification you'd like.  We know there's only one
        // JList in the hierarchy we're searching, so we can look up by
        // class with an instance of ClassMatcher (a very simple
        // implementation of abbot.finder.Matcher).
        Component list = getFinder().find(new ClassMatcher(JList.class));

Abbot will search the available GUI hierarchy until the first match is found (you can use the abbot.finder.MultiMatcher interface if there might be more than one match). You can customize the matching criteria to whatever fits your needs. You can also optimize the search by providing a starting component as the first argument to find.

In order to play actions on the list, we want a ComponentTester object that knows how, so we can create a JListTester (or let the ComponentTester do it and cache the result for future tests).


        JListTester tester = new JListTester();
        tester.actionSelectRow(list, new JListLocation(1)); 
        // or select by value
        // tester.actionSelectRow(list, new JListLocation("two"));
Note the use of JListLocation here. Abbot supports the concept of component-specific locations, which means any given component may provide internal substructure (cells in a table, rows in a tree, items in a list, etc). This substructure is identified by Location classes. For example, a JTableLocation may be constructed with a Point (raw coordinates), a [row, col] pair, or a String value for the desired cell. At this point the selection has finished and any listening events propagated, so you can check the results:

        // We could also use an instance of ClassMatcher, but this shows
        // how you can put more conditions into the Matcher.
        JLabel label = (JLabel)getFinder().find(labeledList, new Matcher() {
            public boolean matches(Component c) {
                return c.getClass().equals(JLabel.class)
                    && c.getParent() == labeledList;
            }
        });
        assertEquals("Wrong label after selection",
                     "Selected: two", label.getText());
    }
Now run the test. It will fail, because we haven't implemented anything in the LabeledList to update the label when a selection is made. We do so now.

    public LabeledList(String[] initialContents) {
        // ...
        list.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent lse) {
                label.setText("Selected: " + list.getSelectedValue());
            }           
        });
    }
Recompile and the test should now pass.

See the full source code for the component example.LabeledList, and its TestCase, example.LabeledListTest.

This project runs on
SourceForge Logo