When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, January 26, 2005

Advanced Techniques with NUnitAsp, Part 2

By Tim Stall


  • Read Part 1

  • 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:

    public class TestBase : WebFormTestCase
    {
        public TestBase()
        {
        } //end of con
    
        public void CheckPage(string strExpectedUrl) 
        {
           WebAssertion.AssertEquals("Expected page does not match actual page. " + 
                                     "Expected=[" + strExpectedUrl + "]. Actual=[" + 
                                     Browser.CurrentUrl.ToString() + "].", 
                                     Browser.CurrentUrl.ToString(),strExpectedUrl);
        } //end of method
    } //end of class
    

    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
    public void SetValues(string[] astrValues)
    public string GetSelectedValue()

    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:

    #if DEBUG
    using ...
    namespace WebMain
    {
        public class TestUCDropDown : System.Web.UI.Page
        {
            ...
        }
        public string Subject
        {
            get {return this.LblSubject.Text;}
            set{this.LblSubject.Text = value;}
        }
    }
    #endif
    

    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:

    public void IISReset() {
        string strFile = "IISRESET";
        ProcessStartInfo psi = new ProcessStartInfo();
        psi.WindowStyle = ProcessWindowStyle.Hidden;
        psi.FileName = strFile;
        Process p = System.Diagnostics.Process.Start(psi);
        p.WaitForExit();
    }
    

    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() {
       System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
       System.Threading.Thread.CurrentThread.CurrentUICulture= new System.Globalization.CultureInfo("en-US");
    }

    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.
    Perhaps its biggest drawback to NUnitAsp is that it only tests the code behind classes in a Web application and not client-side code, like JavaScript. It also requires properly formed HTML, not merely HTML that renders in Netscape or IE. You can confirm the validity of your HTML pages with the online validator at http://validator.w3.org/.

    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


  • NUnitAsp website: http://nunitasp.sourceforge.net/ (version 1.5.1)
  • NUnit website: http://www.nunit.org/ (version 2.2)
  • TestDriven.Net website: http://www.testdriven.net/
  • Online Html Validator: http://validator.w3.org/
  • Test Driven Development in Microsoft.Net, James W. NewKirk and Alexei A. Vorontsov
  • Pragmatic Unit Testing in C# with NUnit, Andrew Hunt and David Thomas
  • Happy Programming!

  • By Tim Stall


    Attachments:


  • Download the complete code examined in this article (in ZIP format)
  • About the Author


    Tim Stall is a Chicago-based technical consultant for Computer Sciences Corporation (www.csc.com), a global IT services company.



    ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article