Fredrik Normén's Blog - NSQUARED²
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Microsoft Most Valuable Professional
     .Net Framework - ASP.Net - Architecture - Development
NOTE: This list of posts will only list the 15 latest posts, to see the rest, select from the Archives located in the menu to the left. The RSS will only list the 10 lastest posts.
Model View Presenter - Use test against Views in ASP.Net

Category:  Other

Something that can be difficult when writing an ASP.Net Web Forms etc is to use TDD (Test Driven Development) or Unit testing to test the logic put into a Web Form (View). By using some patterns, such as the MVP pattern (Model View Presenter, which is now retired and split into Supervising Controller and Passive View ;)) it could be easier to write tests for our ASP.Net applications. I will focus on the Supervising Controller where we separate the logic into both the View (for handle some code to access UI controls) and into controllers (also called Presenter). The more complex and important logic will be put into the controllers. This will make it easer for us to test the View.

 

The following figure is a simple form where we have two TextBoxes for tile and for the body of a news message; we also have a CheckBox to let us specify if we want to see the news now or later. There are two buttons, one Save button to save the News and one Delete button to remove a News that we are editing. I will use a very simple form in this post only to show you the principal of using the Supervising Controller (Note: This is my way of implementing the pattern, there are different ways to do it.).

 

 

[Fig 1]

 

The first thing I will do here is to create an interface that represents the Form/View above (each View have its own interface).

 

public interface INewsView : IView

{

   int NewsID { get; }

 

   string NewsTitle { get; set; }

 

   string Body { get; set; }

 

   bool Display { get; set; }

}

 

If we take a look at the interface, we can see that there is a member for each UI element in the Form. The NewsID property will be used to get the ID for a news (if we are editing news). As you can see the INewsView above implements the IView interface. The reason is that most View’s will have the same kind of “behavior”. Here is the code for the IView interface:

 

public interface IView

{

   IViewController Controller { get; set; }

}

 

The IView have three members that are common for all Views. We have the Controller (which will return a Controller that has the logic for the View).

 

Before I will show you how to write tests against the View, I will go through the whole implementation of the pattern, so you will at least see how it will look like when it’s done. I think that will make it easier for you to understand the tests (But have in mind if you use TDD, write the test first and then the implementation ;)).

 

The following code is my Web Form (View):

 

<body>

    <form id="form1" runat="server">

    <div>

   

        Title<br />

        <asp:TextBox ID="titleTextBox" width="400" runat="server"/>

       <br /><br />

       

        Body<br />

        <asp:TextBox ID="bodyTextBox" Rows="5" Columns="50" TextMode="MultiLine" runat="server"/>

        <br /><br />

       

        Options<br />

        <asp:CheckBox ID="dispalyCheckBox" Text="Display post" runat="server" />

        <br /><br />

 

        <asp:Button ID="saveButton" runat="server" Text="Save" OnClick="saveButton_Click" />&nbsp;

        <asp:Button ID="deleteButton" runat="server" Text="Delete" OnClick="deleteButton_Click" />

 

    </div>

 

    </form>

</body>

 

If we take a look at this Web Form in the Design mode or at runtime, it will look like the Fig 1. The following code is the code-behind (separated code file) of the Web Form that implements the INewsView:

 

public partial class _Default : Page, INewsView

{

 

    private NewsController _newsController;

 

    public int NewsID

    {

        get

        {

            int id;

 

            if (Int32.TryParse(Request.QueryString["ID"], out id))

                return id;

            else

                return -1;

        }

    }

 

    public string NewsTitle

    {

        get { return this.titleTextBox.Text; }

        set { this.titleTextBox.Text = value; }

    }

 

    public string Body

    {

        get { return this.bodyTextBox.Text; }

        set { this.bodyTextBox.Text = value; }

    }

 

    public bool Display

    {

        get { return this.dispalyCheckBox.Checked; }

        set { this.dispalyCheckBox.Checked = value; }

    }

    public IViewController Controller

    {

        get { return this._newsController; }

        set { this._newsController = value as NewsController; }

    }

 

    public _Default()

    {

        this.Controller = new NewsController(this);

    }

 

 

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!Page.IsPostBack)

            this._newsController.LoadView();

    }

 

    protected void saveButton_Click(object sender, EventArgs e)

    {

        this._newsController.SaveNews();

    }

   

    protected void deleteButton_Click(object sender, EventArgs e)

    {

        this._newsController.DeleteNews();

    }

}

 

As you can see the interface of the INewsView is implemented. If we take a look at the NewsID property, you can see that the implementation will get the ID of the News from a QueryString, the NewsTitle will get and set the titleTextBox control’s Text property etc. By using the Supervising Controller pattern, we let the View have the logic to set value to the UI elements because we don’t want the Controllers to know about the UI elements of the Web Form, so we let the View give the Controllers the value from the View by using the interface of the View (more about this later). As you can also see in the code above, the View will have the responsibility to specify which Controller it should use, this is done in the constructor of the View (The View is passed as argument to the constructor of the Controller, which will make it easy for the Controller to “interact” with the view). I could of course create a base class that can automatically create an instance of a Controller out from a configuration file so we don’t need to care about the instantiation of our Controller. It will also make it possible to easily replace our controller with a new one by only changing the type in a config file, but in this example I decide to make it simple to give you the basic understanding of the Supervising Controller pattern.

 

Note: Most of the cases the implementation of the Controller property will be the same. In this case this code could be placed within a base class. But the base class in this case need to inherit the Page class, and to make our test code correct, we need to create a Mock Views that will inherit our base class also, which will have a dependency to the Page class. In my case I don’t want that kind of dependency, and to solve it we could use a Test Double. But in this post I will make the code simple as possible so I skip that part (the ideas is still only to show you how we can test our view).

 

The following code, is the implementation of the Controller used by our view (NewsController):

 

public class NewsController : ViewController, INewsController

{

        private const int NO_ID = -1;

       

        private INewsRepository _newsRepository;

 

        public NewsController(IView view) : base(view)

        {

            this._newsRepository = new NewsRepository();

        }

 

        public NewsController(IView view, INewsRepository mockNewsRepository) : base(view)

        {

            this._newsRepository = mockNewsRepository;

        }

 

        public override void InitView()

        {

        }

 

        public override void LoadView()

        {

            if (this.NewsView.NewsID > NO_ID)

            {

                INews news = this._newsRepository.GetById(this.NewsView.NewsID);

 

                this.NewsView.NewsTitle = news.Title;

                this.NewsView.Body = news.Body;

                this.NewsView.Display = news.DisplayNews;

            }

        }

 

        public void SaveNews()

        {

            this._newsRepository.Save(this.CreateNewsFromView());

        }

 

        public void DeleteNews()

        {

            this._newsRepository.Delete(this.CreateNewsFromView());

        }

 

        private INews CreateNewsFromView()

        {

            INews news = new Nsquared2.News.DomainModel.Model.News();

 

            news.ID = this.NewsView.NewsID;

            news.Title = this.NewsView.NewsTitle;

            news.Body = this.NewsView.Body;

            news.DisplayNews = this.NewsView.Display;

 

            return news;

        }

 

        private INewsView NewsView

        {

            get { return (INewsView)base.View; }

        }

}

 

To not make this post longer as it already is, I will only describe some of the implementation of the Controller (I think you can figure out the rest by your self).

The main purpose of the Controller is to move logic out from the View into the Controller (only to make it easy for us to test our logic), and also to access our domain model.

 

public override void LoadView()

{

   if (this.NewsView.NewsID > NO_ID)

   {

      INews news = this._newsRepository.GetById(this.NewsView.NewsID);

 

      this.NewsView.NewsTitle = news.Title;

      this.NewsView.Body = news.Body;

      this.NewsView.Display = news.DisplayNews;

   }

}

 

The LoadView implementation will get the ID from the View by using the NewsID property (This will get the ID from a QueryString, if the ID is not specified we don’t try to get a News) and get a News object from our Repository. Then the code will map the entity object to the View. As you saw in the code example of our View, each property is responsible to set the value to a UI control, but that is the only logic we will put into our View. We could also in the LoadView make a call to a DataBind method, that will let the View bind the data to its UI controls. To implement the DataBind, we can for example add it to the IView interface. The implementation of the binding, will be handled by the View.

 

To get data out from our View and map it to our News entity, we simply use the property of our View interface (INewsView):

 

private INews CreateNewsFromView()

{

   INews news = new Nsquared2.News.DomainModel.Model.News();

 

   news.ID = this.NewsView.NewsID;

   news.Title = this.NewsView.NewsTitle;

   news.Body = this.NewsView.Body;

   news.DisplayNews = this.NewsView.Display;

 

   return news;

}

 

Now when you have seen most of the implementation we can take a look at how we can write our unit-tests. First we need to create a Mock for our View. The Mock View should of course implement the same interface as our View. When we have our Mock View created we can start writing our test to test the View:

 

[TestMethod]

public void CheckIfNewsViewLoadedCorrectly()

{

    MockNewsRepository mockNewsRepository = this.GetMockNewsRepositoryWithDate();

 

    MockNewsView mockNewsView = new MockNewsView();

 

    mockNewsView.MockID = 1;

 

    mockNewsView.Controller = new NewsController(mockNewsView, mockNewsRepository);

 

    mockNewsView.LoadView(); //Simulate the Page_OnLoad event

 

    Assert.AreEqual("Test 1", mockNewsView.NewsTitle);

}

 

In the code above, I also have a Mock object for my repository that the NewsController will use, and I also have a MockID property added to the MockNews object. The reason I use a MockID, is because the NewsID is a get property, and have no set “method”. So to set the value of the NewsID, I implemented and use the MockID in my test code. To make sure the data is loaded for the View, a LoadView method is implemented to "simulate" the OnLoad event of the View. If we want to "simulate" the Save and Remove button's click event, we can simply add methods to the Mock View that will do the call to the Controller's Save- and DeleteNews method. We can also if we don't want to go through the mock view, make a call directly to the controller’s methods instead. In my case I want to try to reflect my test as it was the client who uses the real view (but because I don't use events in the mock, I implement methods that I call instead).

 

Note: The important thing with the test is to test the logic that is moved from the view into the controller. So if you use TDD you will probably first create tests against the controllers instead of the view.

 

If we need to extend our View with more logic, we simply put the logic into our Controller, and by doing that we can write tests against the logic and make sure it will work, by doing that we have tested our View.

 

You can download the source code here.

 

Summary

 

In this post you have seen one way to implement the Supervising Controller pattern, which will make it possible to write test during our development of a View.

Posted: Tuesday, April 24, 2007 - 14:29 GMT+1    Print     E-mail    Comments (6)
   fredrik.nsquared2.com - 2007