Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Build your own CAB Part #2 - The Humble Dialog Box

When last we left our hero, D'Artagnan was chasing the evil Lady de Winter across the breadth of France trying to intercept her on her dastardly mission when he found himself beset by disparate responsibilities within the tight confines of a single autonomous view with no room for sword play.  D'Artagnan, being a perspicacious young man, quickly sees a way to separate the numerous concerns he's facing by opening his attack with...

The Humble Dialog Box 

I'd say the very first concept to grasp in software design is separation of concerns.  Divide and conquer.  Eat the elephant one bite at a time.  Learning how to decompose a bigger problem into a series of approachable goals.  I'd rather work serially on a screen by completing one simple task before moving onto the next instead of working with all aspects of a screen in parallel.  Division of responsibility for easier programming is a major consideration by itself, but there's another piece of motivation almost as important.  Because user interface code can be very complex to debug and is prone to change based on user experience, I really, really want to extend granular unit testing with automated tests as far into the presentation layer as I possibly can. 

Traditionally, user interface code has appeared to repel all but the most serious attempts at test automation.  Automated testing against UI code was just flat out deemed too much work for the gain.  That equation has changed over the last several years with the rise of architectures inspired by The Humble Dialog Box from Michael Feathers. 

Here's the canonical example of the Humble Dialog Box I use to explain the concept.  Say you have a user screen for some sort of data entry.  You have a requirement that reads something like:

If the user attempts to close the XYZ screen without saving any outstanding changes, a message box is displayed to warn the user that they are discarding changes.  If the user wishes to retain the outstanding work, do not close the screen.  The message box should not be shown if the data has not been changed.

It's not that complex of a requirement really, but it's the kind of thing that makes a user interface client easy and convenient to use.  We want this code to work.  The code for it might look like this:

        private void ArrogantView_Closing(object sender, CancelEventArgs e)

        {

            // Let's not worry for now about how we figure out the screen has unsaved data

            if (isDirty())

            {

                bool canClose = MessageBox.Show("Ok to discard changes or cancel to keep working") == DialogResult.OK;

                e.Cancel = !canClose;

            }

        }

That code really isn't that complex, but let's think about how we could automate a test for this requirement to run within our regression test suite.  That little bitty call to MessageBox.Show() and the modal dialog box that results is a serious pain to deal with in an automated test (it is possible, and I've done it before, but I'd strongly recommend you keep reading before you run off and try it).  Observing the UI getting closed or not is also tricky, but I think the worst part is that to test this logic you have to fire up the UI, navigate to the screen, change some data on the screen, then trigger the close screen request.  That's a lot of work just to get to the point at which you're exercising the code you care about.

Now, let's rewrite this feature as a "Humble" view, but before I show the new code, let's talk about the Humble view philosophy.  The first thing to do is to put the view on a diet.  Any code in a WinForms UserControl or Form is almost automatically harder to test than it would be in a POCO.  A Humble view should be the smallest possible wrapper around the actual presentation code.  Going farther, I don't want implementation details of the view mechanics to leak into other areas of the code, so I want to hide the View behind a POCO-ish interface.  All that being said, the abstracted interface for our View could look like:

    public interface IHumbleView

    {

        bool IsDirty();

        bool AskUserToDiscardChanges();

        void Close();

    }

The view is also "passive," meaning that it doesn't really take any actions on its own without some sort of stimulus from outside the view.  I'll discuss handling user events in depth in a later chapter, but for now let's just say that the view simply relays user input events to somewhere else with little or no interpretation. 

One of the goals of a Humble view is to separate responsibilities.  As in most designs, we want to assign different responsibilities to different areas of the code.  In this case, we want to pull behavioral logic out of the view and into non-visual classes.  If we put the view itself on a diet and pull out anything that isn't directly related to presentation, that extra code that implements things like behavior and authorization rules has to go somewhere.  In this case we're going to move those responsibilities into a Presenter class:

    public class OverseerPresenter

    {

        private readonly IHumbleView _view;

 

        public OverseerPresenter(IHumbleView view)

        {

            _view = view;

        }

 

        public void Close()

        {

            bool canClose = true;

            if (_view.IsDirty())

            {

                canClose = _view.AskUserToDiscardChanges();

            }

 

            if (canClose)

            {

                _view.Close();

            }

        }

    }

In particular, look at the Close() method.  Some user event causes a call to the OverseerPresenter.Close() method.  Inside this method we check the "dirty" state of the IHumbleView member and potentially ask the user to discard changes before proceeding to close the actual view.  It's just about the exact same code, only now we can write an automated unit test to express this logic -- with just a little help from our good friend RhinoMocks.

    [TestFixture]

    public class OverseerPresenterTester

    {

        [Test]

        public void CloseTheScreenWhenTheScreenIsNotDirty()

        {

            MockRepository mocks = new MockRepository();

            IHumbleView view = mocks.CreateMock<IHumbleView>();

 

            Expect.Call(view.IsDirty()).Return(false);

            view.Close();

 

            mocks.ReplayAll();

 

            OverseerPresenter presenter = new OverseerPresenter(view);

            presenter.Close();

 

            mocks.VerifyAll();

        }

 

        [Test]

        public void CloseTheScreenWhenTheScreenIsDirtyAndTheUserDecidesToDiscardTheChanges()

        {

            MockRepository mocks = new MockRepository();

            IHumbleView view = mocks.CreateMock<IHumbleView>();

 

            Expect.Call(view.IsDirty()).Return(true);

            Expect.Call(view.AskUserToDiscardChanges()).Return(true);

            view.Close();

 

            mocks.ReplayAll();

 

            OverseerPresenter presenter = new OverseerPresenter(view);

            presenter.Close();

 

            mocks.VerifyAll();

        }

 

        [Test]

        public void CloseTheScreenWhenTheScreenIsDirtyAndTheUserDecidesNOTToDiscardTheChanges()

        {

            MockRepository mocks = new MockRepository();

            IHumbleView view = mocks.CreateMock<IHumbleView>();

 

            Expect.Call(view.IsDirty()).Return(true);

            Expect.Call(view.AskUserToDiscardChanges()).Return(false);

 

            // No call should be made to view.Close()

            // view.Close();

 

            mocks.ReplayAll();

 

            OverseerPresenter presenter = new OverseerPresenter(view);

            presenter.Close();

 

            mocks.VerifyAll();

        }

    }

So I know what you might be thinking, what have I really gained here?  Let me try to answer this:

  • Orthogonality.  We've moved behavioral logic out of the actual view.  We can change the presentation or the behavior independently.  That is a big deal. 
  • The screen behavior is easier to understand.  I'm going to argue that this is a case of Reg Braithwaite's Signal to Noise Ratio in code (basically, expressing the intent of the code with little code that isn't directly related to the intent).  When I want to understand the screen behavior, that behavior is the only signal I care about.  Seeing (object sender, CancelEventArgs e) everywhere in the middle of the behavioral code is noise.  The converse is true as well when I'm working on the presentation itself.
  • The screen behavior is easier to test and modify.  That's enough by itself to justify the Humble View style.  What if this closing behavior changes tomorrow with a requirement to set a user preference to never ask users to discard changes?  If I'm working in a Humble View style, I can probably make that screen behavior change completely, including unit tests, in the Presenter class by itself without ever having to fire up the user interface until the very last sanity check.  Verifying little behavior changes with NUnit is a far, far tighter feedback cycle than doing the save verification by firing up the user interface and doing the manual input steps necessary to exercise the functionality.  Tight feedback cycles == productivity. 
  • By extending the reach of granular and automated unit tests farther into the potentially complex user interface code, we can drastically slow down the rate of screen defects getting through to the testers.  If nothing else, we can knock down all the common uses of the user interface quickly through tests to give the testers more time to break the application with edge cases and exploratory testing.

 

An astute reader will note that we didn't write any unit tests for the View.  I'll show an example in later chapters of testing the View itself, but the philosophy in general is to make the hard to test view code so simple as to be reliably verified by inspection alone.  I.e., you should make the View code so simple that it's almost hard to screw it up. 

 

A Taxonomy of Humble Views 

Arguably the first Humble View is the original Model View Controller architecture handed down, as basically everything good in software developer seems to be, from the Smalltalk community.  As I've mentioned before, you can read about the evolution of the Model View Controller (MVC) pattern and the formulation of the Model View Presenter (MVP) patterns that we're mostly talking about in this series as user interface toolkits changed.

D'Artagnan's masterful implementation of the Humble Dialog Box vanquishes his foes, but he knows he'll need trusted companions now that the evil Lady de Winter surely knows he is in pursuit.  Fortunately, D'Artagnan's trusty three companions are riding hard to join him.  D'Artagnan smiles to himself and imagines his friends coming around the bend in the road:

  1. Supervising Controller -- I'm here to help the View with the harder cases!
  2. Passive View -- I only do what I'm told
  3. Presentation Model -- Just do what I do

D'Artagnan sees dust rising in the air, a traveler is coming...

 

To be continued in Part 3:  Supervising Controller

     

QUICK NOTE:  I showed the MessageBox stuff happening as a consequence of calling a method on the IView interface.  In the past I've also used some sort of IMessageBoxCreator interface to create message boxes directly.  There are advantages either way.


Posted Wed, May 23 2007 7:29 AM by Jeremy D. Miller

[Advertisement]

Comments

DeveloperDan wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 8:33 AM

Great lesson Jeremy.  I've known it's wrong for me to have so much code in the button_click events, but I've struggled with the best way to do that.  Any chance you'll provide sample code downloads?

ScottC wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 8:45 AM

I'm hoping to see how to make the most of StructureMap as you continue this series also -- Thanks Jeremy!

Jeremy D. Miller wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 8:50 AM

@DeveloperDan & @ScottC -

Whoa!  One thing at a time.  I've got stuff for both topics, I've just got to get through more bad Three Musketeer references first.

Ben wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 11:54 AM

Thanks for this series. I'm just starting to build my first larger, multiple-view, complex application in .Net, and I'm very interested in overall design guidelines like these. Thanks!

Shane Courtrille wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 12:19 PM

Awesome second post

Dustin wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 1:44 PM

Thank you for writing this series.  I'm very much looking forward to reading the rest.  Also, thanks for StructureMap.  It makes my job much easier.

Dan wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, May 23 2007 4:18 PM

Great post,

I would love reading your view  on hooking up the M with the V and the P using an Ioc container.

Roy Tate wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Sun, May 27 2007 1:43 AM

I always ask my fellow developers to write any production code so that we can use it from the command line or the web as well as from Windows Forms.  This usually forces some separation.  :-)

Eli Lopian wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Fri, Jun 1 2007 11:36 AM

Although MVC/MCP is a really nice Pattern, like many patterns, you have to know when to use them. For simple screens they are over-designed. Here is how d’Artagnan handles those simple dialogs (and manages to test them)

See www.elilopian.com/.../the-humble-dialog-and-famous-musketeers

Jeremy D. Miller wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Fri, Jun 1 2007 1:24 PM

Eli,

It was a nice post, but I don't think you ever put enough importance on separation of concerns.  Yes, TypeMock is clever as hell and it makes some of the "Mock Driven" design go away, but separation of concerns is important even if it isn't necessary for testability.  My fear with TypeMock is that people will use it as a crutch for bad design.

Eli Lopian wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Fri, Jun 1 2007 4:22 PM

Jeremy,

I want to Quote You

"but let's think about how we could automate a test for this requirement to run within our regression test suite.That little bitty call to MessageBox.Show() and the modal dialog box that results is a serious pain to deal with in an automated test"

This is what you based the need to change the design. I am challenging this. You never talked about separation of concern in this article or said that the design was bad - only that you cannot automate the tests.

Now to quell your fears:

Having a good design is one of the responsibilities of the developer / team leader. You must refactor your code to get the best design. Seperation of concerns is one of the many design factors, as well as YAGNI, SRP and more. This should have nothing to do with the tools you use.

Tests are a safety net that allows you to change your code/design, not twist your arm into using only one Pattern.

Using this technique will allow you to refactor the code into MVP (in the future, when you really need it), but add value to your customers by having the feature simply designed. That is what agile is about.

Jeremy D. Miller wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Fri, Jun 1 2007 5:25 PM

Eli,

Fair enough, but I most certainly did talk about responsibilities and separation of concerns in the other posts in this series.  The MessageBox is a simplistic example, but an easy one to use.  I would go for MVP in all but the most simple screens.

Going to an IMessageBoxCreator frankly isn't all that difficult, and helps more in full blown integrated testing as well.

"Having a good design is one of the responsibilities of the developer / team leader. You must refactor your code to get the best design. Seperation of concerns is one of the many design factors, as well as YAGNI, SRP and more. This should have nothing to do with the tools you use."

Cool, now *you* go say that somewhere please.  You know full well that people are interpreting TypeMock and your "no designing for testability any longer" statements to mean that they don't have to take as much care with design.  

Eli Lopian wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Fri, Jun 1 2007 5:42 PM

Where ever did you get that idea from:

"people are interpreting TypeMock and your "no designing for testability any longer" statements to mean that they don't have to take as much care with design. "

The opposite is correct. I have always said -> Business Drives Design as opposed to being testable drives design.

You know very well that people are interpreting "Testable" to mean, "if I am Testable then I have a good design", so I don't have to think of designing any more.  I recall that TDD should be:

Write Test -> Write Code -> Refactor.

Not:

Design (for testability)->Write Test->Write Code.

JohnV wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Sat, Jun 2 2007 11:29 AM

I don't quite understand this code. If the screen is Dirty and the user Decides NOT To Discard the Changes, shouldn't we keep the screen open. In that case wouldn't  "DoNOTCloseTheScreenWhenTheScreenIsDirtyAndTheUserDecidesNOTToDiscardTheChanges" be a better name? I used NMock2 to do this and so I had a line in there to show that Close is NOT Called:

Expect.Never.On( mockview ).

       Method( "Close" );

Is there a way to do something similar for RhinoMocks? I know this is a record replay type of thing so if we don't record that action we are saying that it does NOT happen?

Pascal Laurin wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, Jun 6 2007 1:35 PM

@Jeremy

I must agree with Eli that it's important not to put the priority on:

Design (for testability) ->Write Test->Write Code

before

Write Test -> Write Code -> Refactor

I guess the experience you gathered lead you to sometimes skip a few steps the rest of us still need tackle before really understand *why* it's better to design the application in *that* specific way.  And mind you, every project has their own problems and there is just not the one solution for all the cases.

I think your Build your own CAB series should be taken only as an example and not *the* CAB solution for every project.  But that's the readers’ job to be objective about it.

That's the way I fell reading it and I do like your posts a lot.  I'm sure I'll be looking back at your CAB design when ever I'll start a new project.

@Eli

If you never had problem dealing with MessageBox in your testing then fine but that might not always be the case.

At my current job we needed some kind of automated acceptance testing.  I must tell you that the MessageBox problem was one of the many problems related to UI design and usage we had while implementing our testing framework.

Again, if your needs are not the same than ours then using the MessageBox class directly might be Ok.  Just keep in mind that it may be just another technical debt you're introducing into your code base.

Matt Wynne wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Wed, Jun 13 2007 3:12 PM

Jeremy,

Thanks for your insightfull blogs posts about the MVP pattern recently, you have been a guiding light amongst a lot of confused and confusing interpretations of this pattern as I try to implement it on our application.

Having had to go and refactor some of our earlier (and slightly misguided) implementation of the pattern I wrote up what I'd learned here[1] - I'd love to know your thoughts.

[1]mattwynne.wordpress.com/.../mvp-smells

Pascal Laurin wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Thu, Jun 14 2007 8:56 AM

@Matt

I've read your post and it's good but do I understand you avoid compositing your presenters as much as possible?  That you are re-using (copying) code over and over to new presenter in your 1-1 presenter / view model?

Actually that is a question I had for some time now...

@Jeremy

How do you do the presenter for very complex screen?  I bet it is composition of many reusable presenters if possible.  I'm not sure if you talked about that in another post, might have missed it.

Jeremy D. Miller wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Thu, Jun 14 2007 9:39 AM

@Pascal,

Exactly what you said.  Even if you can't reuse the sub-Presenters, it's good to keep the size of the main Presenter down.  

Matt Wynne wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Sat, Jun 23 2007 5:31 AM

@Pascal,

So far we've managed to use an inheritance hierarchy within the Presenters assembly to manage duplication - so if I have two pages that both need to look up and display a Widget from a widgetId in the querystring, then I'll put that code into a WidgetPresenter base class from which both those pages' presenters inherit.

What we found when we had 'too many presenters spoiling the broth' was that sometimes we had two presenters independently making similar, potentially expensive, calls to the model in order to render different bits of the page. By ensuring that there was only one main guy in charge of presenting the page, it's easier to see when this is happening, and easier to test for it.

Matt Wynne wrote re: Build your own CAB Part #2 - The Humble Dialog Box
on Sat, Jun 23 2007 5:36 AM

I moved my blog, by the way, so the above link may not work for ever. Go here to find it instead, if you're interested:

blog.mattwynne.net/.../mvp-smells

Jeremy D. Miller -- The Shade Tree Developer wrote Build your own CAB Part #10 - Unit Testing the UI with NUnitForms
on Tue, Jun 26 2007 9:57 PM

I&#39;ve long, long since left the rails of &quot;Build your own CAB&quot; topics and wandered off into

Jeremy D. Miller -- The Shade Tree Developer wrote Build your own CAB #11 - Event Aggregator
on Fri, Jun 29 2007 11:12 AM

I will finish &quot;Build your own CAB&quot; at least before Acropolis hits and makes it all obsolete

Jeremy D. Miller -- The Shade Tree Developer wrote Build your own CAB #13 - Embedded Controllers with a Dash of DSL
on Fri, Jul 6 2007 12:40 PM

Just to continue the world&#39;s longest run on sentence. Before I start, here&#39;s the table of contents

on Fri, Jul 13 2007 5:28 AM

Aside: I guess this post is really about mock frameworks rather than mocks, but I didn't want to break

Noticias externas wrote The mess that mocks can make
on Fri, Jul 13 2007 6:02 AM

Aside: I guess this post is really about mock frameworks rather than mocks, but I didn&#39;t want to

Jeremy D. Miller -- The Shade Tree Developer wrote Build your own CAB #14: Managing Menu State with MicroController's, Command's, a Layer SuperType, some StructureMap Pixie Dust, and a Dollop of Fluent Interface
on Tue, Jul 24 2007 5:18 AM

The title is a mouthful and accurately implies an alarmingly high jargon to code ration, but I just didn&#39;t

Jeremy D. Miller -- The Shade Tree Developer wrote The Build Your Own CAB Series Table of Contents
on Wed, Jul 25 2007 9:21 PM

Yes, this is overdue. Here is an introduction and table of contents to my &quot;Build Your Own CAB&quot;

Jeremy D. Miller -- The Shade Tree Developer wrote Build your own CAB #14: Managing Menu State with MicroController's, Command's, a Layer SuperType, some StructureMap Pixie Dust, and a Dollop of Fluent Interface
on Thu, Jul 26 2007 10:57 AM

The title is a mouthful and accurately implies an alarmingly high jargon to code ration, but I just didn&#39;t

Jeremy D. Miller -- The Shade Tree Developer wrote Resources from my DevTeach talks
on Thu, Nov 29 2007 12:03 PM

To everybody that attended one of my talks at DevTeach this week. All of the materials are now online

Rob Kitson wrote Keeping UI construction DRY
on Fri, Jan 25 2008 2:47 PM

Keeping UI construction DRY

江南白衣 wrote View to Presenter Communication(转)
on Sat, Aug 2 2008 7:40 AM

Alright, I've purposely hid the View to Presenter communication in my previous posts on Supervising Controller and Passive View because I thought that subject was worthy of its own post. As I see it, there are 2 1/2 basic ways to communicate screen events

Add a Comment

(required)  
(optional)
(required)  
Remember Me?