Carl Erickson, Atomic Object
Scott Miller gave a presentation on the Presenter First technique of organizing and developing large GUI applications in a test-driven fashion.
Presenter First originated with Michael Marsiglia (formerly of AO) and Brian Harleton (X-Rite) on the X-Rite Intellitrax project. It’s been extended and refined by developers at Atomic Object and Burke Porter Machinery on a variety of projects.
What we want when we build GUI apps: correctness, scalability, portability among platforms, customer-driven, test-driven.
What gets in the way: testing GUIs, commercial tools, complex libraries, mixing presentation and business logic.
“Thin GUI” minimizes the code in the GUI so it doesn’t need automated tests.
First cut: separate Model and View classes, model contains view, model making calls into the GUI. Still a problem as it couples the model to the view.
Michael Feathers Humble Dialog is a major step forward in the thin GUI approach. Model uses an interface that the dialog implements. There is still a problem though. The dialog can call the model when an event happens. But this makes the model responsible for both the business logic and the flow of the application. The model is too fat. And it’s harder to test. The coupling between model and view is still pretty tight.
New goal: complete separation of view and model, segregation of control and business logic. Model View Presenter is a design pattern that does this. Martin Fowler has a good description of MVP on his website.
Presenter knows both Model and View. Neither knows Presenter. View generates events for Model and Model generates events for Presenter. Easy to write unit tests on the Model – it’s mostly a data structure.
Presenter First uses a variant on MVP. Presenter knows both Model and View. View and Model don’t know each other, or the presenter. They publish events that the Presenter subscribes to. The Presenter represents user stories of the app. Model and View are completely de-coupled. Presenter First MVP variant uses Observer pattern. Model and View are subjects, and Presenters are observers.
Where do you start coding? (M, V, or P)
Starting with the model is the worst choice. Very infrastructure-first’ish, with the usual problems. It’s better to delay working on the model until the user stories define the need.
Starting with the view seems very logical as it is what the user sees. All users stories appear to be written in terms of the view. Unfortunately this can be a costly mistake to make. Customers generate a high rate of change requests for the view. The focus is on the interface details. Instead, most user stories can be read in terms of the logic of the application, independent of the widgets in the view. Focusing on the view first makes it very easy to put more code in the view. This means the view then requires testing, which is hard.
The last alternative is the presenter. The presenter encodes the logic of the application. User stories describe what the presenter does. It’s simple to write unit tests on the intentions of the presenter. The code is pretty simple, and the model and view can both be mocked. Developing the presenter first creates a specification for the model and view. No more code is built than is needed.
Steve P observed that all the control flow goes through the presenter.
Scott then started with Visual Studio to demonstrate building an application Presenter First in C#.
Rule #1 Start with the presenter.
Presenter constructor takes an interface for model, and an interface for view. Using interfaces for model and view has the usual advantages, but the most important for PF is to make it easy to mock the model and view when testing the presenter.
Rule #2 Makes interfaces for everything – except presenters.
Presenter doesn’t need a public interface. Everything else does for mocking and testing.
NMock generates code for the concrete mock class (you don’t have to write this). You define the expectations in a unit test, fire the event that the model or view would be emitting, and the unit test library takes care of the rest.
Errors found in unit testing the presenter are generally control flow or application logic errors, sometimes model errors.
Unit tests on the presenter test the functionality described in a user story.
This is a form of what Martin Fowler refers to as interfaction testing, in contrast to state testing.
After the presenter is done, either all related stories, or just a single story, we move to the view. Implementing the view is the eye candy reward for doing the presenter first. We usually implement a quick view and don’t spend a lot of time on the interface. This gets the feature into the clients hands more quickly for feedback. User feedback goes into the story backlog.
The view fires events as actions for the widgets (clicks, keyboard, etc). These are the events that the presenter is subsribed to.
The view is so thin that it can be manually tested. Or if the budget and risk allows, an automated GUI testing tool could be used.
Steve P suggested adding break points everywhere, running tests while removing breakpoints until green bar, then seeing what break points remain to detect untested code. We keep our models as simple as possible. Models that would need this type of analysis would be decomposed for better testability and single responsibility. Brian H recommended Clover for measuring test coverage in conjunction with automated unit testing.
If you catch yourself feeling it’s necessary to make assertions on the private state of the model it usually indicates the model is doing too much. Decomposition ala SRP is called for. Single Responsibility Principle says there should only be one reason for a class to change.
Rule #3 Swear an oath to live by SRP
A large app can’t be built as a single giant MVP triad. A recent project Atomic Object and Burke Porter completed consisted of approximately 100 presenters, 120 models and 120 views. This project was 6 developers working for approximately 9 months.
Models are decomposed according to SRP. MVP triads communicate with each other through their models. This keeps models easy to test. Models are decomposed into helpers, etc (other non-model classes).
Putting it all together
Where are the M, V, and P objects created? Some alternatives:
1. In the main function. This works ok for small apps:
IModel model = new Model(); IView view = new View(); new Presenter(model, view);
Event registration will prevent the presenters from being garbage collected, even if they are created in a method that returns.
2. Use a tool like Spring that lets you declaratively construct your triads.
The biggest advantage of this approach is testability. Missing the creation of a presenter is not immediately obvious. Interfaces offered by Spring let you test for proper composition.
Order dependency between MVP triads is easier to handle in Spring. Spring handles the ordering.
Scott finished the talk by running the puzzle app he showed examples from during the talk.