TDD and Java Swing
It’s been awhile since I’ve written any Java Swing code, and doing so now is making me feel a little stupid. Test Driving the development of Swing hasn’t improved much since the last time I did this.
Back in 2003, Jeff Waltzer and I decided to experiment with TDDing a Swing application to see if GUIs could be test-driven. It wasn’t always easy, but we convinced ourselves that they could. We started out rolling our own Swing test framework, but soon found ourselves immersed in AWT threading issues and Swing peculiarities. So, since we seemed to be headed down the road of reimplementing JfcUnit, we decided to switch and use the real thing.
Today, I started with JfcUnit. It hasn’t changed much; the last update seems to have been in 2004. That’s OK, I thought. Swing hasn’t fundamentally changed since then. Since I haven’t been doing Swing programming, certainly my understanding hasn’t changed since then. Finding a JTextField by name was easy enough:
private Component findFieldByName(String fieldName) { NamedComponentFinder finder = new NamedComponentFinder(JComponent.class, fieldName); Component field = finder.find(uut, 0); return field; }
But replacing the contents turned out to be messier:
private void replaceFieldContents(Component textField, String newValue) { getHelper().enterClickAndLeave(new MouseEventData(this, textField, 3)); getHelper().sendString(new StringEventData(this, textField, newValue)); getHelper().sendKeyAction(new KeyEventData(this, textField, KeyEvent.VK_ENTER)); }
Perhaps there’s an easier way, but I haven’t found it yet. The good news is that the same code worked for typing into an editable JComboBox. The bad news is that I couldn’t figure out how to select an entry from the drop-down of the same JComboBox. Grepping the web didn’t turn up much help.
It did turn up a some SD-West 2006 slides by Elliotte Rusty Harold. The Abbot API looked easier to drive, so I tried a version of my tests there. Finding a field by name isn’t much harder:
private Component findFieldByName(final String fieldName) throws ComponentNotFoundException, MultipleComponentsFoundException { ComponentFinder finder = BasicFinder.getDefault(); return finder.find(uut, new Matcher() { public boolean matches(Component potential) { return fieldName.equals(potential.getName()); } }); }
and could probably be further simplified with a NameMatcher class. Replacing the contents of a JTextField was really easy:
private void replaceFieldContents(Component field, String newContents) { JTextFieldTester tester = new JTextFieldTester(); tester.actionCommitText(field, newContents); }
but it doesn’t seem to work with editable JComboBoxes. How do you do that?
Selecting a JComboBox is really trivial:
Component selector = findFieldByName("CommPortSelector"); JComboBoxTester tester = new JComboBoxTester(); tester.actionSelectItem(selector, "COM2");
I do wish I could type into the editable part of the Combo Box, but I think I’ll stick with Abbot.
P.S. How do you get WordPress to leave your code formatting alone? It appears that the only way is to go back and re-edit with the GUI editor turned off.
The folowing will get you the text field part of the combo box in most LAFs.
From there you can use JTextFieldTester to select, replace, type, etc.
Abbot was used to bootstrap itself, so I guess all the “documentation” for using it to do TDD is encompassed in its own unit tests.
NOTE: the first argument to the JTextField find should be the combo box, which narrows the search to descendants of the combo box.
There are a number of simple matchers provided in the library, NameMatcher is among them.