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.