Advanced Techniques with NUnitAsp, Part 2
By Tim Stall
In Part 1 we discussed the merits of unit tests for the GUI portion of a Web application and examined the basics of NUnitAsp, a tool designed to help with said tests. In this second and final part we'll look at performing more advanced tasks with NUnitAsp.
Abstracting Common Functions to a Base Class
After calling
Browser.GetPage()
, the next thing is to check that the web application did not redirect to
another page (such as a global error page if the page load fails). We can do this with the following line of code:
WebAssertion.AssertEquals(Browser.CurrentUrl.ToString(),strURL);
|
However because every test we write will need this, let's abstract it to a common base page:
|
Notice how the base page now inherits from WebFormTestCase
. By creating the method CheckPage()
here,
it will be accessible to all tests. Armed with this base class you'd change your testing class's declaration from:
[TestFixture] public class TestBasic : WebFormTestCase
|
To:
[TestFixture] public class TestBasic : TestBase
|
Using Wrapper Pages
NUnitAsp can only directly access WebForms, not User Controls or the
HttpContext
(which contains
items like Session values). However, by wrapping these things with a helper WebForm, NUnitAsp can indirectly reference
them. For example, suppose we wanted to test a User Control, UCDropDown.ascx
, that had a DropDownList and Label Web
control. Imagine that the User Control has the following methods and property:
public string Subject |
We could create a WebForm, TestUCDropDown.aspx
, put the User Control on it, and then add extra controls to
access the User Control's properties. That is, in order to create a unit test for the User Control's Subject
property I'll create the TestUCDropDown.aspx
WebForm so that it has not only the UCDropDown.ascx
User
Control on it, but in addition a TextBox, Label, and Button. This TextBox will be used to set the value of the User Control's
Subject
property when the Button is clicked; the Label will be used to display the property's value.
Because NUnitAsp can easily manipulate the Web controls on a page, it can use these to indirectly
manipulate the User Control. The screenshot below shows the WebForm in action.
While this is great for testing, we obviously don't want these kinds of pages in production code. We can solve that problem
by enclosing the wrapper WebForm in #if DEBUG #endif
directives so that it only appears in the DEBUG code, not
the release version. For example if you put #if DEBUG
at the top of the codebehind and the #endif
at the bottom, then the entire page will only be available during debugging. The code below shows these concepts all put
together:
|
Besides just testing User Controls, this technique can also handle HttpContext
utilities classes and
manipulating the HttpContext
object itself. For example, if you had a utility class to validate querystrings,
or if you needed to manipulate the Session and cache values, you could wrap these with a WebForm, access them through the
WebForm's controls, and then build unit tests with NUnitAsp. The Wrapper
folder in the code download provides
several examples of this.
Ensuring that Each Test Resets to a Baseline
A basic principle in testing is that each test starts from the same baseline and that one test should not affect another. For web applications this means that we don't want the Application, Session, or cache values generated from one test to persist into another. You could use a wrapper page, as discussed with the previous technique, to call methods that reset the state. Another solution, which is slower yet more thorough, is to reset IIS with the command line
iisreset
,
which will reset the state for the entire application. You can programmatically call this using the System.Diagnostics.Process
class, like so:
|
Besides resetting the cache and Session, you may also need to reset the Culture and UICulture of the current thread. For Globalization, the current thread has a Culture that determines how its dates and currencies will be formatted, and a UICulture that determines which resources files to use. If you're testing a global application, you'll want to ensure that you reset these after each test by setting the current thread like so:
public void ResetCulture() {
|
Depending on which tests need resetting, you could call these methods individually from the tests themselves, or you could
put them in the TearDown()
method of either the class or the base class (for a global scope).
Creating simple Integration and Functional Tests
There are certainly legitimate GUI unit tests, such as testing for dynamically added controls or a configurable menu User Control. However, because in an N-Tier application, the Presentation layer often integrates all the layers below it, it is easy to associate GUI tests with Integration and Functional tests. By controlling the presentation layer, NUnitAsp implicitly offers the ability to write simple Functional and Integration tests too. An example of a functional test would be coordinating a user's actions across an entire business process flow. An example of an integration test would simply be having the page load correctly, which confirms that the application can pass through all the tiers to the database and back.
The downloadable code shows a sample in the Flow
folder where PageA
prompts you to select an
animal from the drop-down. This takes you to the confirmation PageB
, which displays the selected animal.
You can write a test that mimics each step performed by the user, and therefore provides a basic functional and integration
test. Although this example is trivial, the concepts can be extended to include many complex business processes.
While these tests do not replace powerful (and expensive) third part tools, they are great for quick tests, and certainly better than nothing if your team simple doesn't have the financial resources, or full-time testers.
Running your Tests in the Visual Studio Debugger
Every developer appreciates being able to step through and test code with the Visual Studio .NET debugger. However because of how NUnitAsp accesses the web application, the
AspTester
objects will be null
if you step
through with the debugger. One solution is to download the free TestDriven.Net add-in
for Visual Studio.Net. This add-in lets you enter into any method in debug mode by simply putting your cursor in that
method and right-clicking.
Summary
In this article we saw seen how NUnitAsp can provide Unit tests for GUI components, and even make simple Functional and Integration tests. NUnitAsp obviously has a lot of good things going for it, including:
- Is a free tool,
- Integrates directly into NUnit, so it's already familiar for many developers,
- Is intuitive to use because it simply provides a class library to control the WebForm and lets you use that library programmatically in your favorite .Net language, as opposed to requiring you to learn a whole new testing application,
- Can write tests quickly with just a few lines of code, and
- Is open source and therefore can allow for improvements and extensions faster than third-party licensed tools.
NUnitAsp is a great tool, especially for GUI Unit tests. However it is not a substitute for the rest of the testing process, or an excuse to write tightly coupled code that can only be tested from the GUI. You should be using other testing frameworks, such as NUnit, to test the backend Business and Database layers. (For more information on NUnit be sure to read Test Driven Development Using NUnit in C#.) Because it's free, and quick to learn, NUnitAsp is a great program to add to your .Net toolkit.
References
Happy Programming!
Attachments:
About the Author
Tim Stall is a Chicago-based technical consultant for Computer Sciences Corporation (www.csc.com), a global IT services company.