Test-driving presenter-first design

My notion of using lively wireframes as tests for a model/view/presenter style UI leads to finished tests like this:

The test is driven by an OmniGraffle Pro slide show annotated with test assertions. I used the test for test-driven development. For example, the last green box was red not long before I started this post. In order to make it green, I had to make the following changes, in roughly the order shown by the arrows:

That felt like straightforward, unexciting coding, which is what I want from TDD. The driving test is (arguably) business-facing. What of unit tests?

In model/view/presenter, you're not expected to write unit tests for the thin view. The application (model) objects are unit tested like any normal object. The presenters are typically tested by putting mocks on either side, replacing the view and application. I didn't do that. Here's why.

Consider the code that responds to the clicking of the Run button:

    @When (USER_WANTS_TO_CONVERT_A_DIRECTORY_RIGHT_NOW)     public void runConversion(AnnouncingObject sender) {         myView().clearResultsBox();         myView().activateResultsBox();         myApp().convert(myView().getInputFolder(), myView().getOutputFolder());     }

Here's a typical presenter test, using mocks in place of the ConversionView and ConversionApp:

    public void testRunningConversion() {         mockView.expects(once()).method("clearResultsBox")                 .withNoArguments();         mockView.expects(once()).method("activateResultsBox")                 .withNoArguments();         mockView.expects(once()).method("getInputFolder")                 .withNoArguments()                 .will(returnValue("/tmp/dropzone"));         mockView.expects(once()).method("getOutputFolder")                 .withNoArguments()                 .will(returnValue("/tmp/upload"));           mockApp.expects(once()).method("convert")                 .with(eq("/tmp/dropzone"),                       eq("/tmp/upload"));           announce(USER_WANTS_TO_CONVERT_A_DIRECTORY_RIGHT_NOW);     }

My first impression, looking at that, is that it's too much work for the code it tests. That's probably overstating it, since the test is stylized and straightforward to write. In fact, it's too straightforward: the test and the code are mechanical transformations of one another. Moreover, the transformation happens in one step (one test ⇒ one complete straight-line method) because all the "iffyness" of the code gets factored out into a profusion of different methods and the declarations of which announcements each responds to. (This is like the way switch statements can be factored into objects of different classes.)

Because of all this, the unit test, even if written first, seems to lack the idea-generation virtues a unit test ought to have. You're not interspersing the coding of a method's internals with thinking about what visible behavior it should have. The behavior that mattersis dispersed, and the method's internals are its behavior (since all it's foris telling other objects what to do).

It's the wireframe test, not the unit test, that produces Aha! moments. It forces you to think about what counts: "when the user pokes at this button here, what should happen to all the bits of UI the user can see?" Before I thought of the idea of wireframe tests, I found it easy to overlook that a change in one window ought to produce changes in another. Nothing rubbed it in my face like the wireframes do.

However, these wireframe tests look an awful lot like traditional GUI tests, and they may have their great weakness: many different tests share knowledge of the UI, so a product change that deliberately falsifies a bit of that knowledge will break many tests. I have some ideas about dealing with that problem in a way that GUI-driving tests cannot. Will they work out? Who knows?

My development preference is probably unchanged: put off the UI (and especially UI tweaking) in favor of getting the business logic right. In the case of this program, I did a lot of work on the conversions before there was anything more than the crudest possible command-line UI. However, I've noticed and heard something in the past couple years: the trust of the business people is driven by how well the UI matches what they imagine of the finished product. Consider the novice product director—which is most of them, these days, at the start of projects. Thrust into a new situation, promised early and frequent delivery of business value, and largely unable to distinguish "the product" from "the UI", she demands—and gets—a UI first. I have faith that many product directors can, in time, come to see the product as being about business rules rather than about UI. But by the time, any damage due to working UI-first will have been done. Therefore, I think it prudent to find ways to make what the business wants (screen images) serve the team's need to have tests drive their code. That's why I'm hot on wireframes.