ASP.NET VERSIONS: 1.x
Powerful Open Source Unit Testing
By Rob Walling
Test Driven Development (TDD) is a growing trend in the software industry. In the past year, a multitude of articles have been published touting the benefits of the test-driven approach. While the benefits of TDD over a more traditional, code-first approach are still being debated, it s safe to say that automated unit testing has become a widely accepted practice. Being able to test the basic features of an application with a single button-click is simply too appealing to ignore.
This article, however, does not discuss TDD specifically, because the topic was well covered by Ian Blackburn in his article Test Driven Development with ASP.NET. Rather, this article serves as further exploration of NUnitAsp, an open source tool commonly used in TDD, but equally relevant to code-first methodologies (see Ken McNamee s introduction to NUnitASP).
Testing Web-based user interfaces is often done manually, which ultimately means a lot of time-intensive pointing and clicking. There are automated UI test tools available, but most have prices well beyond the reach of today s tight IT budgets. One alternative, NUnitAsp, extends the well-known open source unit testing framework NUnit to allow for scripted testing of ASP.NET user interfaces. Using the .NET language of your choice, you can easily create scripts that programmatically input data and click the buttons of your Web pages.
NUnit is an open source unit testing framework written in C#, and ported from the Java-based framework JUnit. If you ve read any of the recent articles on Test Driven Development, you re probably familiar with this tool. NUnit allows .NET developers to create methods, known as tests, which call production code, feeding the appropriate parameters and asserting the proper results are returned. These tests are contained within Test Fixtures, which are nothing more than .NET classes.
NUnit is commonly used to perform unit testing on the business and data tiers, but because of the complexity of the environment, it is unable to perform tests directly on ASP.NET code. The solution is NUnitAsp, which extends the capabilities of NUnit, creating the appropriate framework to test Web user interfaces.
NUnitAsp tests are written using AspControlTester objects that mimic the ASP.NET Web controls. For instance, to test a TextBox you would use a TextBoxTester. For a Button, a ButtonTester. By instantiating a tester control for each ASP.NET control, a developer can populate a TextBox, select an item from a DropDownList, or click a Button using .NET code inside a controlled testing environment. The concept is easiest to understand in the context of an example, so let s begin with a sample application.
To begin, download the latest version of NUnitAsp, available at http://nunitasp.sourceforge.net/download.html. Installation is a breeze, and is detailed at http://nunitasp.sourceforge.net/installation.html.
The Sample Application
To demonstrate the capabilities of NUnitAsp I ve created a one-page sample application that simulates a basic user registration form (all code used in this article is available from the asp.netPRO Web site; see end of article for download details). The form prompts for an e-mail address, password, and name, and displays a confirmation message if registration is successful, hiding the original form via a panel. The page includes regular expression validation on the e-mail address, and required field validation on the e-mail address and password. It also contains a ValidationSummary to aggregate the validator error messages. A screen shot of the design view is shown in Figure 1.
Figure 1: Design view of the Registration page.
The code-behind contains the event handler for the Register button and a Private method called RegisterUser. In this example, RegisterUser is a stub; however, in a production implementation it would likely contain some database access code. Assuming the user is registered successfully, the panel containing the form is hidden and the panel containing the confirmation message is displayed (see Figure 2).
Private Sub btnRegister_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs)
' Perform server-side error checking
If Not Page.IsValid Then Exit Sub
' Register the user
' Display Confirmation to user
pnlForm.Visible = False
pnlConfirmation.Visible = True
Catch ex As Exception
lblError.Visible = True
lblError.Text = ex.ToString
Private Sub RegisterUser()
' User registration code would be here...
Figure 2: Code-behind for the Registration page.
Preparing to Write a Test
Individual test methods, also known as tests, are contained in .NET classes, also known as Test Fixtures. To test the Registration application, I created a class library in Visual Studio.NET called RegistrationTest. To access the required NUnit and NUnitAsp classes, this project requires references to the two dlls shown in Figure 3. The class file RegistrationTest.vb contains the Test Fixture (see Figure 4).
C:\Program Files\NUnit 2.2\bin\
Figure 3: DLL references needed to use NUnitASP.
Private pnlForm As PanelTester
Private pnlConfirmation As PanelTester
' Form elements
Private valSummary As ValidationSummaryTester
Private txtEmail As TextBoxTester
Private txtPassword As TextBoxTester
Private txtName As TextBoxTester
Private btnRegister As ButtonTester
Protected Overrides Sub SetUp()
pnlForm = New PanelTester("pnlForm", CurrentWebForm)
pnlConfirmation = New PanelTester("pnlConfirmation",
' Form elements
valSummary = New ValidationSummaryTester("valSummary",
txtEmail = New TextBoxTester("txtEmail", CurrentWebForm)
txtPassword = New TextBoxTester("txtPassword",
txtName = New TextBoxTester("txtName", CurrentWebForm)
btnRegister = New ButtonTester("btnRegister",
. . .
Figure 4: The basis of our Test Fixture.
Notice that this class imports three NUnit namespaces. These imports statements are only valid if the references mentioned above have been added. These three namespaces contain the core features used in our Test Fixture.
Next, notice the
Also notice that RegistrationTest inherits from WebFormTestCase. WebFormTestCase is an NUnitAsp class that establishes the context of this Test Fixture and allows it to use objects such as Request, Response, and Server, even though the class is not running in the ASPNET worker process.
Next comes the tester object declarations. Notice I ve declared one tester for each form element, and I ve made the names identical to their ASP.NET counterparts. A complete list of tester objects is available at http://nunitasp.sourceforge.net/api/NUnit.Extensions.Asp.AspTester.html.
Figure 4 concludes with the SetUp method, which instantiates a new instance of each tester object. To instantiate a tester object you must provide a string containing the name of the object to be tested, along with its container. CurrentWebForm, a property of WebFormTestCase, is the container that you will most often use.
SetUp runs before each individual test, so it should be
used to perform common initialization tasks. Its partner method, TearDown, is
not used in this example, but is run after the completion of each test. If you re
familiar with NUnit, you re probably wondering why I chose to override these
methods instead of using the
The First Test
The first test for any Web page should be TestLayout,
which confirms that the appropriate form elements are visible (see Figure 5). Notice
' Tests the basic layout of the form
Figure 5: TestLayout confirms that the appropriate form elements are visible.
The first step in testing the layout is to use the built-in Browser object to retrieve the page from the Web server. After that, we can assert the visibility of each item, ensuring that the Validation Summary and confirmation Panel are invisible when the page loads, and that all of the remaining elements are visible.
Now that the first test is written, let s take a look at how to run it.
Running a Test
NUnit comes with both a console and a Windows Forms interface. Being a UI developer, I prefer to use Windows Forms, and there are two ways to invoke it.
The first is by navigating to the nunit-gui.exe through your Start menu (Start | Programs | NUnit 2.2 | NUnit-Gui). Once the application starts, click File | Open and search for your test DLL (in this case, RegistrationTest.dll).
The second way is to set nunit-gui.exe as a Start Action for your test project in Visual Studio.NET. I prefer this method, because it allows easy step-through of both your test and application code during test execution. To set this up, right-click the RegistrationTest project in Visual Studio.NET and select Properties. Under Configuration Properties in the left panel choose the Debugging menu item. Under Start Action, select the Start External Program radio button and browse to the nunit-gui.exe location, which is C:\Program Files\NUnit 2.2\bin\ by default. Click OK to close the window. Finally, right click on RegistrationTest again and set it as the start up project. Now when you hit F5 in Visual Studio.NET, the NUnit GUI will start.
In either scenario, once NUnit has opened you ll see a hierarchy of your tests in the left panel and empty test results in the right panel. Clicking the Run button will execute the tests and, using color-coding, display whether they have passed (green), failed (red), or been skipped (yellow); see Figures 6 and 7.
Figure 6: The NUnit GUI display; all tests passed.
Figure 7: The NUnit GUI display; one test failed.
After you ve tested your form layout there are many different tests you could write, even for this fairly simple page. The key is to write tests that exercise the significant variations of input to your page.
I ve created the following tests for Register.aspx (these tests are included in the downloadable code; again, see end of article for details):
- Submitting an empty e-mail
- Submitting an empty password
- Submitting an incorrectly-formatted e-mail
- Submitting a valid registration
Because they all function in essentially the same way, I ll examine two of these tests and leave the rest for the reader to explore.
The TestEmptyEmail test begins by getting the Registration page from the Browser object, then setting the Text properties of the three textboxes; note that Email, a required field, is empty (see Figure 8). Calling the Click method of the ButtonTester simulates a user clicking this button in the browser. Our expected behavior is an error message telling the user that Email is a required field. We test this by examining the Messages array of the ValidationSummaryTester object. Because arrays are zero-based, the 0th message is the first one displayed. In this case, we use AssertEquals, which has several overloaded forms. The one used here accepts a message to display if the test fails, and two strings that should be equal. If they are not equal, the test fails.
' Tests the behavior of the form when email is empty
txtEmail.Text = String.Empty
txtPassword.Text = "password"
txtName.Text = "Rob Walling"
AssertEquals("Error message did not appear.",
"Email is a required field.")
Figure 8: The TestEmptyEmail test.
One important note: NUnitAsp uses HTTP GET and POST methods, so client-side form validation does not execute when the Register button is clicked. As a result, you must be sure to use server-side form validation to properly test your validators.
The TestSuccessfulRegistration test once again retrieves Register.aspx using the Browser object (see Figure 9). In this case, the form is filled out correctly, the button is clicked, and the visibility of the confirmation panel is confirmed. In addition, we confirm that the Validation Summary and form Panel are now hidden.
' Tests a successful registration
txtEmail.Text = "[email protected]"
txtPassword.Text = "password"
txtName.Text = "Rob Walling"
Figure 9: The TestSuccessfulRegistration test.
I urge you to explore the numerous articles and tutorials published on NUnitAsp to gain more exposure to this powerful tool; begin with the resources available on the NUnitAsp Web site. Remember that writing a test takes time, but once complete, it works for you each and every time you run it.
Visit these sites for more information on getting started with NUnitASP:
- The Starting Point for NUnitAsp Information: http://nunitasp.sourceforge.net/
- Advanced Techniques with NUnitAsp: http://aspnet.4guysfromrolla.com/articles/012605-1.aspx
- NUnitAsp DataGrid Testing Tutorial: http://nunitasp.sourceforge.net/tutorial/index.html
The sample code referenced in this article is available for download.
Rob Walling is a Microsoft Certified Application Developer (MCAD.NET) with five years of development experience. His areas of expertise include ASP.NET, VB.NET, and Web application architecture. His technical articles appear in various .NET-related Web sites and publications. You can reach him at mailto:[email protected].