Writing unit tests for traditional asp.net code-behind code can be tricky. The code is usually full of dependencies to UI objects and user interactions that present challenges when attempting to write unit tests.
In this article I will describe how to reduce the impact of these dependencies by using the unit test friendly pattern Model-View-Presenter (MVP).
This article assumes basic knowledge of MVP, so I recommend checking out the following Wikipedia article http://en.wikipedia.org/wiki/Model-view-presenter if you are new to MVP.
In short, the main purpose of MVP is to separate pure UI code from the logic used to control the UI code. The benefit of this is much better testability, but also better code reuse. In a proper MVP implementation, the UI for totally different UI platforms may use the exact same presenter class. As an example, a WebForms application may use the exact same presenter as a Windows Forms client.
In this article I will demonstrate how to use MVP to implement simple login functionality in a WebForms application. Figure1 shows the basic solution overview with the necessary projects.
The first thing we have to define is the login page (view), and the login presenter used to control the page.
Code listing 1
From code listing 1 you can see that the login page implements the interface ILoginView which dictates the two methods RedirectToHomePage and DisplayErrorMessage. Also, the constructor of LoginPresenter expects an instance of an object that implements ILoginView.
Passing in the reference as a parameter of type ILoginView gives the presenter full control over the view without having to know anything about the underlying implementation in the view. Based on the value returned back from the model (user), the presenter will simply invoke methods to control the rendering of the view. During error conditions (invalid username/password or locked account) an appropriate error message is passed back to the view method for rendering. If the username and password match, the user is redirected to the home page. As you can tell, all logic has been extracted out to the presenter, and the only code left in the login page is simple rendering code.
The design of the presenter makes it very unit testable.As mentioned above, the presenter expects a parameter of type ILoginView. However, this can be any object that implements this interface. For the purposes of the tests I have created a stub (code listing 2) that we will pass to the presenter.
Code listing 2
The LoginStub class includes two extra properties, MethodExecuted and ErrorDisplayed. These properties are used to check if the presenter invoked the correct methods, and passed the correct parameter to the view. Code listing 3 shows the test scenarios I have defined for the presenter.
Code listing 3