Introduction & Background
Model View Presenter (MVP) is a design pattern used for web as well as Windows application. It is especially used where the application has some user interface for interaction like Winform for Windows app and Webform for web app. In this tutorial, I will show you how to implement MVP in web app as well as Windows app. Another important aspect of MVP pattern is to unit test your whole interface as well as business layer. This really comes in very handy when the application size becomes really very big. The main objective of MVP is Separation of Concern or Loosely Couple the entire system, so that we can test each part of the application.
Using the Code
For example purposes, I will take the Northwind database. I generally use LINQ for accessing database so I have created a dbml for the whole bunch of Northwind database.
Here, we can represent data layer as our model. Frankly speaking, model represents the datalayer and datalayer could be your traditional classes to access database or some services to access database. Here, we will use LINQ classes for our datalayer.
The web interface is very simple in order to make the illustration purpose easy. Let’s talk about our web interface. It will load with all categories and for category selection; it will show the list of products under the selected category. It should have one dropdownlist to hold all the categories. When user selects the category from category listbox
, a gridview
will be shown with all the products under the category. It’s obvious that we have two events; one is pageload
on which all the categories will be shown in the category list; another is selected index change of the dropdownlistbox
on which the products will be populated for the selected category in the products gridview
. Also there should be some list/collection classes to hold the list of category/product.
So for our view, we will create the interface and one delegate. The code is shown below:
public delegate void CategoryChangeEventHandler(object source, CategoryEventArgs e);
public interface IProductListing
{
IEnumerable Categories { get; set; }
IEnumerable Products { get; set; }
event EventHandler ListingCategory;
event CategoryChangeEventHandler ChangeCategory;
}
The code is very simple to understand. We declare two enumerable interfaces to hold the list of category/product and two events; one is listing of category and another is category changing. In ChangeCategory
event, we declare a custom eventargs
class to hold the selected category id. The code for CategoryEventArgs
is shown below:
public class CategoryEventArgs : EventArgs
{
private Int16 _CategoryID;
public CategoryEventArgs(Int16 CategoryID)
{
this._CategoryID = CategoryID;
}
public Int16 CategoryID
{
get
{
return this._CategoryID;
}
set
{
this._CategoryID = value;
}
}
}
Let’s show the code for the presenter
class. The Presenter
class is responsible to do something when view raises an event and presenter will respond to the event. To get the view instance, we pass the view through the presenter’s constructor. This method is called constructor injection – a Dependency Injection method. Then, we will subscribe the view events with methods declared in the presenter. The code is shown below:
public class ProductListingPresenter
{
IProductListing _view;
public ProductListingPresenter(IProductListing view)
{
this._view = view;
this._view.ListingCategory += this.OnCategoryListing;
this._view.ChangeCategory += this.OnCategoryChanged;
}
private void OnCategoryListing(object source, EventArgs e)
{
NorthwindDataContext oDataContext = new NorthwindDataContext();
this._view.Categories = from category in oDataContext.Categories
select category;
}
private void OnCategoryChanged(object source, CategoryEventArgs e)
{
NorthwindDataContext oDataContext = new NorthwindDataContext();
this._view.Products = from product in oDataContext.Products
where product.CategoryID == e.CategoryID
select product;
}
}
Here, you can see that the view is passed through the presenter using constructor injection. Now we subscribe the ListingCategory
event of the view with presenter’s OnCategoryListing
method. Also we subscribe the ChangeCategory
event of the view with the presenter’s OnCategoryChanged
method.
Within these methods, we call the LINQ classes to populate the data and set the data through view’s property. That’s it and we can test our whole application.
At the start, I mentioned that MVP pattern really makes unit testing easier. In context to this example, we don’t need to write the aspx pages to test the application. We will write a mock class to test the application functionalities and later on we put the code in the aspx page.
Here is the code for the mock class:
public class MockProductListing : IProductListing
{
IEnumerable _categories;
IEnumerable _products;
public void TestPageLoad()
{
EventHandler handler = this.ListingCategory;
if (handler != null)
{
handler(this, new EventArgs());
}
}
public void TestProductListingByCategory()
{
CategoryChangeEventHandler handler = this.ChangeCategory;
if (handler != null)
{
CategoryEventArgs oArgs = new CategoryEventArgs(Convert.ToInt16(1));
handler(this, oArgs);
}
}
#region IProductListing Members
public IEnumerable Categories
{
get { return this._categories; }
set { this._categories = value; }
}
public IEnumerable Products
{
get { return this._products; }
set { this._products = value; }
}
public event EventHandler ListingCategory;
public event CategoryChangeEventHandler ChangeCategory;
#endregion
}
In the mock class, we need to implement the view interface. We will test only two events; one is pageload; another is category dropdownlist selected index changed. You can see, I have written two test methods to test these two events.
Finally, we will write the test methods.
[TestMethod]
public void PageLoad()
{
List<category> categories = new List<category>();
MockProductListing _view = new MockProductListing();
ProductListingPresenter _presenter = new ProductListingPresenter(_view);
_view.TestPageLoad();
foreach (Category cat in _view.Categories)
{
categories.Add(cat);
}
Assert.AreEqual(8, categories.Count);
}
[TestMethod]
public void TestProductsByCategory()
{
List<product> products = new List<product>();
MockProductListing _view = new MockProductListing();
ProductListingPresenter _presenter = new ProductListingPresenter(_view);
_view.TestProductListingByCategory();
foreach (Product prod in _view.Products)
{
products.Add(prod);
}
Assert.AreEqual(12, products.Count);
}
Run the test methods and you will find the expected results.
So now, you can realize how MVP helps to test our application code. In the original representation, we just need to copy the mock class’s code to the aspx page.
History
- 8th December, 2010: Initial post