Clearer Views with MVP

 Nov 3, 2006  |   Filed under: software development  |   Tags: .NET, MVP, Patterns, TDD


I am currently working on a new project at work which is supposed to be a basic presentation UI for a database. I decided to use TDD for this project and also to implement the MVP pattern (which I wrote about here). I figured it would be nice to have tests on the UI since the application is mostly UI and database access.

After writing for a while my view interface started to get cluttered with properties and methods, most of them duplicates of each other (I have 9 drop down boxes, just imagine that in the view interface).

Example View:

public interface ISearchView
{
   // Text Box
   string SearchText{ get; }
   bool SearchTextVisible{ set; }

   // Drop Down List
   string SelectedProject{ get; }
   bool SelectedProjectVisible{ set; }
   void AddSelectedProject(string key, string value);

   // And so on, and so on…
}

This was starting to turn into a real ugly and large interface. I would rather return the entire controls so I could access all theire functions. In order to do this my presentation layer would have to have a reference to the System.Web.UI namespace. Although this would be fine for this project my conscious told me that it just wasn't kosher. Besides it would make unit testing horrible.

So what do you do when you don’t know what to do?
You write a test on how you wish it would work.

I came up with this test. It tests how I would fill my projects list in an ideal world. Since I didn't have a list implementation I just added a list interface.

[Test]
public void ProjectListIsFilledProperllyOnInit()
{
  DynamicMock mockView = new DynamicMock(typeof(ISearchView));
  DynamicMock mockProjectList = new DynamicMock(typeof(ISelectableList));

  SearchPresenter presenter = new SearchPresenter((ISearchView)mockView.MockInstance);
  mockView.SetReturnValue("get_ProjectList", mockProjectList.MockInstance);

  mockProjectList.Expect("Add", "0", "Project0");
  mockProjectList.Expect("Add", "1", "Project1");
  mockProjectList.Expect("Add", "2", "Project2");

  presenter.Init();

  mockProjectList.Verify();
}

Alright, so now it seams we need to create ISelectableList and add the methods that we need for the test to pass. (By the way the project list is a drop down list if you haven’t figured it out by now)

public interface ISelectableList
{
   void Add(string key, string value);
}

And now we only have to add the ProjectList property to the ISearchView interface to get the test to pass, yippy! But wait a minute; this doesn’t actually access the UI in any way. It’s true it doesn’t but there’s several ways of solving this once you have the interface.

First you should probablly have a seperate WebPresenter project where you can keep all the System.Web.UI specific stuff. Then you could, for example, make a derived DropDown class like this:

public class PresentableDropDownList : DropDownList, ISelectableList
{
   // You get the picture.
}

In order for this to work we would have to change the class of our drop down list in the code-behind every time we used the designer to create a new drop down. I’ve tried this before and trust me, it isn’t fun and you will get bugs (the nasty kind that bite and tickle).

That is why I decided to create a wrapper instead. Using a wrapper also allowed me to use the base class of DropDownList, ListControl, which is also used by several other lists, thus giving me a wrapper for those as well (how magical).

public class SelectableWebList : ISelectableList
{
   private ListControl m_innerList;

   public SelectableWebList(ListControl innerList)
   {
      m_innerList = innerList;
   }

   public void Add(string key, string value)
   {
      m_innerList.Items.Add(new ListItem(key, value));
   }
}

Now I only had to add the other methods I wanted to expose to ISelectableList and create an ITextBox interface and my view interface cleaned up to the following:

public interface ISearchView
{
   ITextBox SearchText{ get; }
   ISelectableList ProjectList{ get; }

   // And so on, and so on…
}

Much nicer!

You can download the samples i wrote for this post here.