CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Ben Reichelt's Weblog


Learning the Model View Presenter Pattern



Comments

Jeremy D. Miller said:

Ben,

I've used the MVP paradigm on a couple of WinForms clients with very good results. You're absolutely right that you can't get away with only testing the presenters with mocks or stubs, but it makes the UI control logic vastly easier to unit test before you even try the integrated stack. You've always got to write multiple types and layers of tests, but unit testing the presenter logic will remove a lot of bugs quickly before you even attempt to debug through the full stack.

Even without using something like NUnitForms on the views you'll get fewer bugs there because they're thinner and much simpler. The "view" code itself is also easier to test because it doesn't have quite so much coupling to the layers underneath it.

I like to start with just a mockup, code the presenter and flesh out the ISomethingView interface, then build the view last. I've routinely seen new screen features work on the very first try when you work this way.

Here's another WinForms specific example of MVP --> http://codebetter.com/blogs/jeremy.miller/articles/129546.aspx
# November 18, 2005 2:02 PM

breichelt said:

Jeremy, thanks for the comments. It was funny, when I googled for "model view presenter" your post was towards the top, and Scott Bellware had a post on the first page of results where he linked to a post by Jeffrey about the pattern, its a regular codebetter.com cyle!

But I do agree with you about the mvp pattern not being a cure all, but a great first defense about bugs, I'm going to try and incorporate it into my next project at work.
# November 18, 2005 2:42 PM

Jeremy D. Miller said:

Incest comes to mind. I think I met Jeffrey for the first time right after his post.
# November 18, 2005 2:56 PM

Jeffrey Palermo said:

# November 19, 2005 1:11 PM

Dan Bunea said:

Hi,

MVP is a great pattern to test the UI logic and to make the separation of concerns when it comes to UI logic. I have used it very succesfully lately, on winforms, asp.net and even with net cf apps.

However, from time to time I do get the feeling of insecurity about the UI itself, as MVP is a pattern that makes testing the presenter/controller very easy, but the UI can remain completely untested.

For this reason I am thinking about ways to extend automated testing also to the UI. I believe that testing also the View is important for windows.forms apps as they are much more complex then web app views. If you also want to benefit from databinding (and sometimes you're compelled to do it by some third party controls) then testing the view becomes more and more important.

So what do we do about this? One idea I had was to extend the view itself and make the mock. For instance, normally we could have:

public interface IView
{
void SetOrder(Order o);
Order GetOrder();
void DisplayMessage(string message);
}

public class Presenter
{
...
public void Init()
{
this.view.SetOrder(model.Order);
}
...
public void Save()
{
try
{
orderBusiness.Save(view.GetOrder());
view.DisplayMessage("Order saved ok.");
}
catch{
view.DisplayMessage("Order could not be saved.");
}
}
}

public class MockView:IView
{
private Order order = null;
private ArrayList messages = new ArrayList();

...

public void SetOrder(Order o)
{
this.order = order;
}
public Order GetOrder()
{
return this.order;
}
public void DisplayMessage(string message)
{
Console.WriteLine(message);
messages.Add(message);
}
}

Very easy to test. But if in the view I use databinding that means that the view needs to become visible.

public class OrderForm:Form,IView
{
...

public void SetOrder(Order o)
{
this.SuspendBindings();
this.RemoveBindings();
this.order = o;
this.DataBind();
this.ResumeBindings();
}
public Order GetOrder()
{
this.EndCurrentEdit();
return this.order;
}
public void DisplayMessage(string message)
{
MessageBox.Show(message,...);
}
...

private void DataBind()
{
this.txtNo.DataBindings.Add("Text",order,"No");
this.txtDate.DataBindings.Add("Text",order,"Date");
}

private void SuspendBindings()
{
this.BindingContext[order].SuspendBindings();
}
private void ResumeBindings()
{
this.BindingContext[order].ResumeBindings();
}

}

So could we have the MockView actually extend the OrderView itself, se that we could also have more control over testing the view also?

What do you think?

Sorry for the code, It was written on the spot;

# November 21, 2005 4:27 AM

breichelt said:

Dan, I was thinking the same thing as I was learning about the MVP pattern. However, I dont think you can actually have a class that inherits from System.Windows.Forms.Form be hosted inside of nunit and have all of the events and databinding work correctly. Keep in mind that I'm no expert on WinForms, but I believe that it needs to be started as its own process for things to work correctly.
# November 22, 2005 12:31 PM

Dan Bunea said:

Hi,

After doing a little research I found out it is possible to extend the form itself to test with databinding, if needed. I will be publishing the source code on my blog soon.

Thanks,
Dan Bunea
http://danbunea.blogspot.com
# November 23, 2005 3:00 AM

breichelt said:

Dan, I would love to see this code, I'll be watching your blog for it
# November 23, 2005 10:08 AM

Dan Bunea said:



Source code: Download


Lately, I have noticed that the Humble Dialog Box or Model...
# November 28, 2005 3:05 AM

Dan Bunea said:



Source code: Download


Lately, I have noticed that the Humble Dialog Box or Model...
# November 28, 2005 8:29 AM

adam said:

This might be a bit late for you to be interested but; I'm currently looking at MVP/TDD for an internal teach in. The sample I drew up worked in a similar manner to your download. However I spotted a 'bug' in my own code and you appear to have the same issue.

public bool AlbumForm::IsClassical
{
   get{ /* ... */}
   set
   {
       cbClassical.Checked = value;
       txtComposer.Enabled = value;
   }
}

this is asnippet from your production code where you actually have business logic. i.e. setting the classical checkbox state also enables the state of another control. (In my case is was the clearing of a list before populating it.)

My current thinking is that this is wrong and should be 2 properties/methods on the IAlbumView [ComposerEnabled *and* ISClassical] such that setting the current Album on the presenter should invoke both of these methods seperatly.

I've not had enough experience using MVP to 'know' if I'm right :-) so I'm still open minded about it all.
# March 20, 2006 10:49 AM

breichelt said:

Thats a good point adam, but IMO (not knowing much about MVP either) I would think that this is okay, because it still abstracts the concept of IsClassical from the actual view itself.  The presenter simply tells the view that IsClassical is true/false then its up to the view to decide what actions to take because of that.

By adding a ComposerEnabled property, you are letting some implementation details of the view permeate into the presenter. Again, just my (possible naive) opinion :)
# March 20, 2006 10:54 AM

adam said:

> The presenter simply tells the view that IsClassical is true/false then its up to the view to decide what actions to take because of that.

I've thought more about this (and had a re-read of the martin fowler MVP page, which I'm sure has been updated since I last looked). The presenter implements *all* of the biz logic for any UI. The view is not allowed (my understanding of MVP) to "decide" any actions.

Now if I could the the product manager to use FitNess *and* I actually thought it was worth the effort we could have FIT tables to demonstrate what should happen in various UI senarios.

If only I'd gone to the effort of re-reading the Folwer page before my earlier post I could have better stated my thinking [now not then, which would have been then too had I read it, clear :-) ].

Cheers,

adam
# March 20, 2006 3:48 PM

Billy McCafferty said:

# March 30, 2006 6:59 PM

Billy McCafferty said:

# March 30, 2006 7:04 PM
Check out Devlicio.us!

Our Sponsors