The beginnings of a classic ASP Unit test framework

This is the third part of a series exploring how to unit test classic ASP code. In part one, we explored the motivations behind this series and in part two we examined a scenario that will give us context when discussing the framework we are building.

In this part we start building a classic ASP framework that will enable us to test new VBScript/ASP code that we are writing and to facilitate this we create two projects - a C# website project that represents our ASP site and a C# class library project for our unit tests. Note that the Unit test project needs to be nested within the ASP site so that ASP test pages we write can access the classes of main site in the same site and context.

The image shown here has the nested project location highlighted and we will therefore be accessing our testing pages with "(wesbite)/UnitTest/ASPTesting.Tests/".

Our first thing to address is how we will encapsulate the code that we are writing, enabling us to test code in isolation without unnecessary overhead. We will achieve this through VBScript classes and objects with one ASP file per class - these files will sit within the main ASP site and through server side includes we can use those classes / objects in both our main site ASP pages and in our test ASP pages. But how will we test these isolated classes?

Let us start with creating a UnitTester class that we can use as the base of our framework and we save this in the root of the ASPTesting.Tests project. We will want this class to emit HTML based on assertions that we make in ASP test pages that we will be calling manually; we therefore want to make this user friendly and able to provide context about the tests we are performing.

We create a file called "UnitTester.asp" to contain the class and introduce an initialisation sub-routine that will emit some test header HTML:

<%
Class UnitTester
   Public Sub Init(testMessage)

      Response.Write("<style type='text/css'> .unitTest {color: #CF118C;}")
      Response.Write(" .testMessage { padding-right:50px }")
      Response.Write(" .passed { color:green; font-weight:bold; }")
      Response.Write(" .failure { color:red; font-weight:bold; }")
      Response.Write(" .unitTest, .test, .given { font-family:Verdana; font-size:14px; }")
      Response.Write(" .given,  { font-weight:bold; }")
      Response.Write("</style>")

      Response.Write("<div class='unitTest'><h1>Unit testing for ASP</h1>")
      Response.Write("<h2>" + testMessage + "</h2></div>")

   End Sub
End Class
%>

We can now set up a test page that uses this class directly - we have a UnitTest folder in our ASPTesting.Tests project and we place our first ASP test file in there (Test.asp). Note that we have also set up a virtual directory called "Tests" that points to the root of the ASPTesting.Tests folder, enabling us to use a virtual include.

<!--#include virtual="Tests/UnitTester.asp"-->
<%
Dim tester
Set tester = new UnitTester
tester.Init("Testing our unit tester class")
%>

Here we are creating a new instance of the UnitTester class and writing some HTML via the Init() sub-routine. If we call this page directly in the browser we can view the result:

Now we can add some new behaviour to our UnitTester class:

Public Sub Given(message)
   Response.Write("<hr><div class='given'> Given " + message + "</div>")
End Sub

Private Sub WriteTest(passed, message)
   Response.Write("<br><div class='test'>Test: ")

   If passed Then
       Response.Write("<span class='passed'>Passed</span>")
   Else
       Response.Write("<span class='failure'>Failed</span>")
   End If

   Response.Write("<span class='testMessage'> - " + message + "</span></div>")   
End Sub

Public Function AssertAreEqual(actual, expected)
   Dim areEqual, sActual, sExpected
   areEqual = (actual=expected)

   dim sActual, sExpected
   If IsNull(actual) Then
       sActual = "NULL"
   Else
       sActual = CStr(actual)
   End If

   If IsNull(expected) Then
       sExpected = "NULL"
   Else
       sExpected = CStr(expected)
   End If

   WriteTest areEqual, ("Actual value of <b>" + sActual + "</b> is expected to be <b>"
                          + sExpected + "</b>")
   AssertAreEqual = areEqual
End Function

We have added a Given() sub-routine that will enable us to provide context to a series of assertions we are making and we have a new private sub-routine called WriteTest() that facilitates the writing of the test result - it will give us a "Passed" or "Failed" message each time it is called.

Finally we have our first assertion function that tests the equality of two values and calls the private Write Test routine to emit the HTML result.

Wrapping this all up we can now test this functionality with some more code in our Test.asp file:

<!--#include virtual="Tests/UnitTester.asp"-->
<%
Dim tester
Set tester = new UnitTester
tester.Init("Testing our unit tester class")
tester.Given("that we are testing our new framework")
tester.AssertAreEqual 1, 1
tester.AssertAreEqual "this", "this"
tester.AssertAreEqual "yes", "no"
%>

We make a number of assertions here and expect them to have the correct HTML output when we access the Test.asp file manually in a browser:

In the next part of this series we will examine how to automate this test using NUnit so that we need not manually call our ASP pages directly every time we write a test. We will then proceed with our example requirement and show the unit test framework in use whilst adding new assertion functions as we need them.



2 comments:

  1. irenav March 10, 2010 at 2:27 PM

    where's the next section? This is really cool.

     
  2. Justin Davies March 17, 2010 at 1:46 PM

    try this for a list of the classic ASP posts on my blog:
    more test first development - classic asp. Unfortunately that was the last part of the series as I no longer work with an asp codebase. Good luck!