ASP.NET MVC is the new kid in town and everyone wants to use it. What happens though, if you can't migrate your application to ASP.NET MVC (ex: maintenance of a legacy Web Application)?
The purpose of this article is to explain how to test an ASP.NET Webforms Applications. How the Model-View-Presenter pattern is helping us in that, how do we use mock-up libraries to simulate objects like HttpContext and how do we use Visual Studio 2010 Testing Environment.
Testing Web Applications
First of all, when we want to test a Web Application we are considering two types of tests. Acceptance Tests and Unit Tests.
Acceptance Tests are tests from a user prospective and most of the times an automated-browser testing tool is used (like WatiN, Selenium etc.) So we are testing the user experience, if our app works in different browsers, if client-validation works as it should etc.
Unit Tests are tests dedicated to test our code, our business logic and validation, our database interaction and everything that are not related to how data is displayed. Although many times Unit Tests and Acceptance Test may have overlapping areas (example, when testing if a page works simultaneously validation is also being tested) it's important to have both in a project.
For now, let's focus on Unit Tests.
Let's consider the following real world but simple example:
Almost every website has a contact page. The user enters his details and sends them to the server. The server processes the data and informs the user about the result. How can we test a page like this?
Figure 1: Screenshot - Contact Page
Explaining the MVP Pattern
The MVP pattern stands for Model-View-Presenter and doesn't differ a lot from MVC (Model-View-Controller). When an application is developed using the MVP pattern it's more testable because layers are being separated from each other. So each layer's responsibility is clearer thus easier to define test cases to run against. An excellent open-source framework that helps us build a WebForms application using the MVP Pattern is WebFormsMVP. This framework is being used in this example.
An implementation of the page above using the MVP Pattern along with a brief explanation of the pattern its self would be the following.
Model is the class that holds the data of our page. In this case it is a simple class with three properties that are represented by our text fields and one property that holds if the Model is persisted or not to our database.
Listing 1: ContactModel.cs
It's also included just for reference the enProcessState enum.
Listing 2: enProcessState.cs
View, in this case, is the user control that displays the form and defines events – if necessary – that correspond to user actions. Because a view can be implemented in various ways an interface is being defined and the user control is implementing that interface.
Listing 3: IContactView.cs
The implementation of the interface is our user control.
Listing 4: ContactControl.ascx
And our code-behind is the following.
Listing 5: ContactControl.ascx.cs
The presenter is responsible for passing the model to the view and subscribing to events that the view defines in order to execute appropriate actions.
Listing 6: ContactPresenter.cs
In order for UI to be separated from the Logic of our Application we have two (2) projects in our solution. The first is a Class Library project that contains the interface of the View, the Presenter and the Model. The second is the ASP.NET Website that contains the implementation of the view and everything else relevant to that like the ASPX page that contains the UserControl etc.
Figure 2: Project Structure in Visual Studio
Now that our project is fully explained we need to add a Test Project to our solution. Also we need to add the following assembly references to our Test Project:
We also need to add a reference to our Logic Project.
Notice that we don't add a reference to our Web project because the MVP pattern keeps the nicely separated so that we don't have to!
The biggest problem when testing a website is that you have to simulate the request from the client and other things like if the user is authenticated, etc.
This is where a mock-up framework comes handy. A mock-up framework is helping us to create objects or interfaces with any kind of behavior. Let's see it in action.
We will use Rhino.Mocks and to do that we will install the corresponding package from NuGet.
Fire up the Package Manager Console, set as default project the Test project and type
Install-Package RhinoMocks. This will add all the necessary references to our test project.
Now we are ready to mock our HttpContext.
Listing 7: Simulating the HttpContext along with options like if the user is authenticated etc.
This above code is a function that creates a mocked HttpContext, sets whether the user is authenticated and the username of the current user.
So our if we want to test code that checks if the user is authenticated although we don't have an actual request Context.User.Identity.IsAuthenticated will work as expected!
Notice that we can use the Stub method of a mocked object to demand properties or methods of that object return explicit values.
When creating a unit test it's also best to follow the AAA pattern. AAA stands for Arrange, Act and Assert. The method above that mocks an HttpContext is in the Arrange Part of our unit test. The creation of our View and our Presenter is also included in this part.
So let's create those two:
Listing 8: Creating the View and the Presenter inside our Unit Test
Notice that we don't care about the actual implementation of the view and that is why we are mocking the interface. The view could easily be a WPF UserControl, a Silverlight UserControl etc. Testing if the view actually works falls into the area of Acceptance Tests.
Now that we have created our environment, our view and our presenter we have completed the first A – the Arrange part.
The Act part is described in the following code snippet:
Listing 9: Act part of our Unit Test
Let's walk through those lines. First we create our model and set the appropriate values that correspond to our test case. Then we set the model to our view (which was created in the Arrange part) and next we use an extension method that is defined by RhinoMocks Framework and allows us to raise the Contacting event of our view.
What we expect is that the presenter has subscribed to that event, created the contact and change the ProcessState property of our model.
This is the final A of our Arrange-Act-Assert pattern. Is this unit test it's fairly simple
We expect the value of ProcessState property to be enProcessState.Saved.
Just for reference here is the entire Test file
Listing 10: ContactPresenterTests.cs
As you can see we are using
[TestInitialize] attribute of Visual Studio Test Framework to initialize our Presenter and our View.
Our Presenter is being initialized just before every test case.
Also when creating test methods many times is convenient to start with “Should” or “ShouldNot” based on if this method tests a valid scenario (example: user enters valid data and contact should be saved) or an invalid scenario (example: user enters invalid data and contact should not be saved).
This article was an introduction to testing a Web Application. You can download the code if this article and inside you will find a few extra goodies (which are out of scope of this article) like validating user's input, displaying messages across the application, using Attribute-based validation like MVC, Dependency Injection etc.
I hope this was a good kick-start on ASP.NET WebForms testing.
External libraries used:
John Katsiotis is a Microsoft MVP in ASP.NET since 2008 and .NET Developer for the past 4 years. You can find more info at his personal site or follow him on Twitter
This author has published 3 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.