Exploration Through Example

Example-driven development, Agile testing, context-driven testing, Agile programming, Ruby, and other things of interest to Brian Marick
191.8 167.2 186.2 183.6 184.0 183.2 184.6

Wed, 30 Mar 2005

Design-Driven Test-Driven Design (Part 3)

For background, see the table of contents in the right sidebar.

The story so far: the way methods have clustered in a Fit DoFixture has persuaded me that I need to make some new classes. Heaven help me, the image that comes to mind is viral particles budding off a cell. (The image on the right shows flu viruses.) I also know that I want to move toward the Model View Presenter pattern, because it should let me get most of the UI code safely under test. And I'm driving my work using scripts / tests of the sort a User Experience (UX) designer might write.

The first time I worked through this script, I created a single class per method-cluster. For the methods that referred to the "patient detail interaction context", I created a PatientDetailContext class. The fixture would send it commands like chooseToEnterANewRecord and query results with methods like thePatientDetailContextNames.

The problem was that no test ever drove me to split that one class into the View and Presenter classes. If you think about it, that makes sense. In order to preserve design flexibility and keep detail from obscuring the essence, the test script I'm working from is deliberately vague about the details of the user interface. Since Model-View-Presenter calls for the View to do little more than relay low-level GUI messages over to the Presenter, the programmatic interface between the two (which I think of as like the mitotic spindle in cell division - what's with me today?) has to name a bunch of detail that the script leaves out. Where can that detail come from?

I think it has to come from my knowledge of my end goal. So in this do-over, I'll start by assuming such low-level messages. But rather than jump into two classes right away, I'll start by budding out the Presenter and have the DoFixture act as the View (the self-shunt pattern).

Let's take two steps in the script to work from:

choose patient Betsy with owner Rankin
now the patient detail context names Betsy

Here would be the object structure and pattern of communication for the first line:

A portion of the DoFixture acts as a kind of mock View for the Inpatient Presenter, pretending to respond to user actions. Its implementation of choosePatientWithOwner looks like this:

    public void choosePatientWithOwner(String animalName, String ownerName) {
        inpatientPresenter.setAnimalNameText(animalName);
        inpatientPresenter.setOwnerNameText(ownerName);
        inpatientPresenter.pressChoosePatient();
    }

Those look rather like UI messages. The Inpatient Presenter knows that when "Choose Patient" is clicked, it should tell the Patient Detail Presenter about it. The Patient Detail Presenter in turn has to update its View. Its View is, again, the DoFixture. So the Presenter calls a new method on the DoFixture, setPatientName:

    // patient detail view
    private String patientName;

    public void setPatientName(String patientName) { // called by presenter
        this.patientName = patientName;
    }

Now when the next test line claims that "now the patient detail context names Betsy", Fit calls a query method that returns the right result:

    public String thePatientDetailContextNames() {
        return patientName;
    }

That works (given the right code in the presenters). Having gotten green, I can now refactor. There are two uglinesses in this code.

  1. Here's the top of the file that declares the InpatientPresenter:

    package com.testingthought.humble.ui;

    import com.testingthought.humble.fixtures.dofixtures.InteractionDesign;

    The Presenter isn't a testing class - it lives with other UI classes - but it depends on a testing class.

    That's easily fixed by creating an interface InpatientView that's in the UI package, having the Presenter depend on it, and having the DoFixture implement it.

  2. The DoFixture is now talking about detail like text entry fields and button presses. That kind of thing obscures what I think an interaction design DoFixture ought to be: a facade over a collection of cooperating presenters and views. It shouldn't do work itself - it should just tell other objects to do work.

    (In general, I'm starting to think of DoFixtures as adapters that translate between object-speak and procedural-speak. In procedural-speak, you don't want to manage specific pointers to objects. Instead, you want to refer to them in a looser way. For example, sometimes you want to refer to them implicitly, trusting the reader or DoFixture to know which one you meant.)

By an amazing stroke of luck, I can now extract the View-ish methods into full-fledged mock View objects that implement my new interface. They will:

  1. take interaction design actions and turn them into GUI-level messages to the Presenter.

  2. accumulate GUI-level messages from the Presenter and remove detail irrelevant to an interaction design.

Like this:

I won't show the code itself, but you can find it in a zip file. As before, the build.xml file in the top level runs the Fit tests. To read it, start with the DoFixture's choosePatientWithOwner method and trace through the calls.

We've now achieved the View/Presenter separation. The mock Views do conversions. The "from" part of the conversion comes from the Fit tests, but where does the "to" part of the conversion come from? What tests directly drive the the decision about what messages to send from Views to Presenters? Well, what always drives the coding of classes distant from the "top" of the system? - unit tests.

That's kind of an interesting inversion. Usually, we think of the code that handles UI widgets as being on the outside of the system, the part closest to the user. But here that code is utility code, not really different from database access code. By burying utility code behind a layer (be it a persistence layer or a DoFixture derived from an interaction design), we let people thinking about larger issues ignore the grotty technology that lies behind the business value of the system.

Next: interlacing Views and mock Views. Plus, I finally get around to learning to program Cocoa.

## Posted at 08:21 in category /fit [permalink] [top]

About Brian Marick
I consult mainly on Agile software development, with a special focus on how testing fits in.

Contact me here: marick@exampler.com.

 

Syndication

 

Agile Testing Directions
Introduction
Tests and examples
Technology-facing programmer support
Business-facing team support
Business-facing product critiques
Technology-facing product critiques
Testers on agile projects
Postscript

Permalink to this list

 

Working your way out of the automated GUI testing tarpit
  1. Three ways of writing the same test
  2. A test should deduce its setup path
  3. Convert the suite one failure at a time
  4. You should be able to get to any page in one step
  5. Extract fast tests about single pages
  6. Link checking without clicking on links
  7. Workflow tests remain GUI tests
Permalink to this list

 

Design-Driven Test-Driven Design
Creating a test
Making it (barely) run
Views and presenters appear
Hooking up the real GUI

 

Popular Articles
A roadmap for testing on an agile project: When consulting on testing in Agile projects, I like to call this plan "what I'm biased toward."

Tacit knowledge: Experts often have no theory of their work. They simply perform skillfully.

Process and personality: Every article on methodology implicitly begins "Let's talk about me."

 

Related Weblogs

Wayne Allen
James Bach
Laurent Bossavit
William Caputo
Mike Clark
Rachel Davies
Esther Derby
Michael Feathers
Developer Testing
Chad Fowler
Martin Fowler
Alan Francis
Elisabeth Hendrickson
Grig Gheorghiu
Andy Hunt
Ben Hyde
Ron Jeffries
Jonathan Kohl
Dave Liebreich
Jeff Patton
Bret Pettichord
Hiring Johanna Rothman
Managing Johanna Rothman
Kevin Rutherford
Christian Sepulveda
James Shore
Jeff Sutherland
Pragmatic Dave Thomas
Glenn Vanderburg
Greg Vaughn
Eugene Wallingford
Jim Weirich

 

Where to Find Me


Software Practice Advancement

 

Archives
All of 2006
All of 2005
All of 2004
All of 2003

 

Join!

Agile Alliance Logo