Click here to Skip to main content
15,881,852 members
Articles / Operating Systems / Windows

MVP - A Basic Demonstration of its Power

Rate me:
Please Sign up or sign in to vote.
3.46/5 (4 votes)
31 Aug 2006CPOL5 min read 41K   381   14   6
A basic demonstration on how to build a Unit Testable, Web and Windows Login UI

Introduction

I'm thrilled by how the Model-View-Presenter Pattern (MVP) is helping me on the "Get-Filters on Wizard, Show-Result" type of report I'm doing on work right now. So after grasping a little bit more of how MVP works, and believe me, it can get quite tricky, I decided to write about it.
I'll illustrate the MVP pattern through a really simple sample, but one that could be used in lots of applications: A login UI. I'll build the UI in the following steps:

  • The Unit test for the UI (Yeah you read it right! I'll start with the UI unit test!)
  • The Windows Forms UI
  • The ASP.NET UI

I'll start with a solution that looks like this picture:

But where is the UI project, you'll ask. That's right, no UI for now. Lets write the first unit test. It'll fill the username, password and try to login.
So I create a new Unit Test (without wizard) in the UnitTests assembly with the name of LoginUITest.
I'll be using mocks for my unit tests. If you don't know about mocks I recommend this article. Mock objects let you simulate a physical object, in the sense that you can create a much simpler object than the one your application is really going to use. By using this technique your Unit Tests get much simpler and by doing so enable you to write more tests, more efficiently. This concept will get clearer once I show some code.
So I write my unit test the way I imagine a login form should behave:

C#
 1:  private ILoginForm view;
 2:  private LoginFormPresenter presenter;
 3:
 4:  [TestInitialize()]
 5:  public void MyTestInitialize() {
 6:      //Create a new view (the one that simulates a form)
 7:      view = new Mocks.LoginFormMock();
 8:      //Create the presenter that will be responsible for
 9:      //the behavior of the view.
10:      presenter = new LoginFormPresenter(view);
11:      //Initialize the presenter.
12:      presenter.InitializeView(ViewInitialization.FirstTime);
13:  }
14:
15:  [TestMethod]
16:  public void TestLoginUI() {
17:      //values to test against.
18:      string targetUser = "heynemann";
19:      string targetPass = "MVProx!";
20:
21:      //Set the view values to the values I want to use.
22:      view.Username = targetUser;
23:      view.Password = targetPass;
24:
25:      //Before invoking the event, I make sure the view
26:      //retained the values I passed in.
27:      Assert.AreEqual<string>(targetUser, view.Username);
28:      Assert.AreEqual<string>(targetPass, view.Password);
29:
30:      //This method is used to simulate a button click.
31:      ((Mocks.LoginFormMock)view).RaiseLoginButtonClickEvent();
32:
33:      //If the Presenter handled the click event
34:      //the user should be logged by now.
35:      Assert.AreEqual<bool>(true, view.IsLoggedIn);
36:  }

Ok, now I have to make this test pass. The next thing to do is creating the classes and interfaces used by my unit test.
You can see that there´s a ILoginForm interface. That interface is responsible for my view. All the forms that represent a Login Form will have to implement this interface, no matter whether they are Web or Windows Forms, or whatever else you can think of that can perform a login operation.
So I create this interface in the UI assembly. Note that this assembly must not have any references to either System.Windows.Forms or System.Web.UI since it´s UI independent.
Basically this interface is the one that will hold my login credentials and if the user has logged in successfully.
So I add to the interface the following properties: Username, Password and IsLoggedIn. The guideline for naming boolean variables is to prefix them with Is, so that´s why I call it IsLoggedIn and not LoggedIn. The ILoginForm interface will declare a LoginButtonClick event as well so our presenter can subscribe to that.
The image for the ILoginForm interface:

The next thing to do is create an implementation of this Interface in Mocks.LoginFormMock. The purpose of this class is to be the simplest implementation possible for the ILoginForm interface.

Ok, now I must build the LoginForm behavior, which basically is initialization and login. So I create the LoginFormPresenter class. One thing to note is that every single presenter is bound to a view. So the default constructor (with no parameters) is private in the case of our presenter. The only public constructor is one that receives an ILoginForm parameter, since this way we can assure that this presenter will be used in the right way.

The LoginFormPresenter will have two operations: InitializeView(View Initialization) and Login. The first method initializes the view and gets as parameter an enumeration indicating whether this is the first time around or not (in ASP.NET scenarios it´s very common for the initialization to be called multiple times, but only in the first some operations should be performed). The second method is private and will get called upon the LoginButtonClick event raised, and after that perform the login operation.

Ok, so now our unit test compiles. This is already a major advancement, in the direction that we probably have all classes nailed right now. So now we go to the core of the Test-Driven-Development discipline: the unit testing.

I run the unit test we just created and it passes just fine. Now we are close to finishing our sample.

I'll build the windows and web UI´s with almost no effort.

Let´s start with the windows UI. I create a Windows Forms project in my solution called Heynemann.LoginSample.Windows.
The UI for the Windows Form is just like this:

Now on to the MVP stuff. Let´s get to the code file of this form and make sure it implements ILoginForm.
So a few important stuff, in the windows forms application the Username and Password properties map directly to the .Text property of the corresponding textbox, and the btnLogin click event handler just calls the LoginButtonClick event passing the view as sender and EventArgs.Empty as parameters. Well, that´s pretty much it. The Windows client is ready, and working.

Now onto the web application. I create a website called Heynemann.LoginSample.Web. Then I open the default.aspx web form and build it´s UI the same as the windows one. Then I do the same process of implementing the interface as I did on the windows form. And that´s pretty much it. Our web interface is working the same as the windows one, with VERY SMALL effort.
For more information take a look at the full-solution source code included in this article.

Hope this helped understand how cool is MVP. For a more thorough reading, please read Martin Fowler´s articles on the subject. You can find them in his website at: http://www.martinfowler.com/

You can view the original blog post that originated this article at http://manicprogrammer.com/cs/blogs/heynemann/archive/2006/08/11/22.aspx. Feel free to leave comments here or there and make sure you sign my blog (good stuff on a daily basis :))

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United Kingdom United Kingdom
Bernardo Heynemann is a senior developer at ThoughtWorks UK in London. He is really into Visual Studio 2008, LINQ and ASP.Net MVC. He's also chairman of Stormwind Project (http://www.stormwindproject.org). He can be found at his blog at http://blogs.manicprogrammer.com/heynemann.

Comments and Discussions

 
GeneralMore code Pin
Judah Gabriel Himango31-Aug-06 17:37
sponsorJudah Gabriel Himango31-Aug-06 17:37 
GeneralRe: More code Pin
Dr Herbie31-Aug-06 22:32
Dr Herbie31-Aug-06 22:32 
GeneralRe: More code Pin
bernardoh1-Sep-06 3:30
bernardoh1-Sep-06 3:30 
GeneralRe: More code Pin
Dr Herbie1-Sep-06 3:41
Dr Herbie1-Sep-06 3:41 
GeneralRe: More code Pin
bernardoh1-Sep-06 3:28
bernardoh1-Sep-06 3:28 
That´s right, the model is only needed when you need to do data manipulation.
For instance, if you had a Page that updates the Customer object, then the view would be the Page itself, the Customer object would be the model and the presenter would be the same as in my sample.
You can say that the model in my sample is the IPrincipal generated when you login.

Bernardo Heynemann
Lead Developer / Architect for Perlink Consulting Brazil

GeneralRe: More code Pin
Judah Gabriel Himango1-Sep-06 4:35
sponsorJudah Gabriel Himango1-Sep-06 4:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.