Sunday, March 7, 2004

Getting into the Swing of TDD

Just came off a tough late night/early morning pairing session with Simon Harris. We're working on a Swing app that will let you wander around the duplication in your code base by reading a Simian log file and referring to the original code tree. We're also trying an experiment where we use TDD and blog the experience so that we can publish the gory details of the construction of the app, on the presumption that anyone cares, of course...:P

Last night's session will make for interesting reading, mainly because we're both pretty clueless about Swing. In keeping with Jon Eaves ideas on spiking and TDD, we mucked around for a while trying to figure out how to use the java.awt.Robot to drive our panels, but we got there in the end. What we are trying to explore is how far we can push TDD, and I, for one, am so sick of web apps that this seemed like a cool idea.

I don't know enough about Swing to understand why a lot of folks seem to hate it these days, but it's always appealed to me as having the potential to facilitate real unit testing. By this I mean that a UI area composed of multiple JPanels should be able to have independently designed and tested each of those panels before assembling the UI and running functional tests over it.

The theory goes something like this:

  • you don't have to write tests for one line delegation methods in my book. So if the UI was a veneer so thin over a fully tested set of non-Swing stuff, I'm happy to just glue the UI onto the layer below with trivial delegation. This is the interesting part - how thin can the UI layer be? So thin that no testing is required?
  • you use a Mediator to coordinate the interaction between panels (which you of course make an interface so you can easymock it for testing), and each panel implements a Colleague interface (again facilitates testing). This means that although there is 2-way comms between the mediator and the colleagues, either end of this pipe can be mocked, allowing testing of each bit independently.
  • you then assemble your UI like any other swing UI and the bits play nice.

We had previously gotten our mediator TDD-ed into existence and were happy with it. We then came to creating the JPanel that contains a list of files that share some duplicated code. This panel was also TDD-ed into existence but with all its behaviour in non-Swing methods. All good. Then it came time to (we thought) just add the JList and off we go. Turns out the amount of code required, with event listeners and a private inner ListModel class left us facing the dreaded NullPointerException at run time. If there is a more glaring signal that your code is crap, I don't know it.

So we came to the conclusion that we would have to actually unit test the UI with events somehow, you know, mouse and keyboard stuff. Yuk. We mucked around for a while trying to figure out how to do this while keeping the test 'headless', but failed dismally. In the end we came to grips with the pixel coordinate stuff required by Robot and plonked our little panel into a JFrame of its own in the unit test and just displayed it and started the Robot clicking on stuff.

So the upshot was that a single panel pops up on the screen and dances about a bit being unit tested against a mock Mediator that catches and verifies the panel's interactions with the rest of the dialog that isn't even there yet. Long story, I know, but I thought it was kinda fun and my blog's been a bit quiet lately.



5 comments:

  1. There are some packages out there that are probably a bit higher level than using Robot directly. We're using Jemmy for this:
    http://jemmy.netbeans.org/
    which I like because we have the full power of using Java in our tests. (JFCUnit offers nice capabilities, but uses XML files for running tests which can get ugly if it's not doing exactly what you want).
    I've got a few more links on my blog to various tools and tips for testing Swing GUIs.
    http://www.blueskyonmars.com/archives/2003/06/09/danno_ferrins_java_gui_testing_tools_roundup.html
    http://www.blueskyonmars.com/archives/2003/09/05/testing_swing_guis_with_junit.html
    http://www.blueskyonmars.com/archives/2004/02/15/swingmultithreaded_unit_testing_tips.html
    Kevin

    ReplyDelete
  2. You don't have to use XML with JFCUnit, you can as well write your tests in pure Java.

    ReplyDelete
  3. Thanks Kevin, I'll check them out!

    ReplyDelete
  4. Check out http://marathonman.sourceforge.net/ is a Swing Acceptance Testing Framework written in Java and Jython Scripts. It also facilitates recording scripts with a nice UI.

    ReplyDelete
  5. Abbot provides a library on top of java.awt.Robot that lets you specify things more sanely than pixel coordinates, e.g.
    actionClick(myButton)
    actionSelectRow(myTree, row 1)
    It can also be scripted at a higher level to avoid depending on compiled code for your tests.
    It is very much tailored to expressing what you want to do on a component as opposed to stringing together a lot of low-level events (although you can always still do that if you need to).

    ReplyDelete