Using the classic ASP unit test framework

This is the fifth part of a series on unit testing classic ASP and in this we will start to use the framework to implement our example user stories we described earlier whilst attempting test-first development. The earlier parts of the series are available at:

Part one: how to unit test classic ASP
Part two: user stories for classic ASP unit testing example
Part three: beginnings of a classic ASP unit test framework
Part four: automating classic ASP unit tests with NUnit

We begin this part by revisiting our user story backlog and identifying the story that we will be working on - we choose one of the easiest:

As a user if I access the system between 13:00 and 14:00, I cannot place an order and the system will provide an appropriate message.

For this we anticipate having an "OrderSettings" VBScript class and properties that will indicate the cut off time and next order time and a function that will tell us whether a user can place an order. Therefore we can write our tests before we write the class itself.
We create a new folder in our Unit Test project called "Orders" and in here we place a new ASP file, "OrderSettings.Test.asp":

<!--#include virtual="Tests/UnitTester.asp"-->
<%
dim tester
Set tester = new UnitTester
tester.Init("Testing Order Settings")

Dim currentOrderSettings
Set currentOrderSettings = new OrderSettings

currentOrderSettings.CutOffTime = "01/01/1999 13:00:00"
currentOrderSettings.NextOrderTime = "01/01/1999 14:00:00"

Dim onePm
onePm = "10/10/2008 13:00:00"

tester.AssertIsFalse currentOrderSettings.CanOrderAt(onePm), "Can Not Order At 13:00"
%>

Here, we are expecting a new OrderSettings class to have a function called CanOrderAt - we pass the date/time in and it returns true or false.
Note that we are using dates here for our time functionality and not strings... we made this choice so that we can use the date types and functions to assist us and this can also be stored reliably in the database.

We now wrap this in a NUnit test fixture by creating a new file called "OrderSettingsFixture.cs" and place this alongside our test file:

namespace ASPTesting.Tests.UnitTests.Orders
{
    [TestFixture]
    public class OrderSettingsFixture : AspFixture
    {
        [Test]
        public void OrderSettingsTest()
        {
            RunAspTest("UnitTests/Orders/OrderSettingsTest.asp");
        }
    }
}

When we run this unit test via a test runner it fails due to the 500 server error produced by the missing OrderSettings class:

Now, we go to our main ASP website and insert a new ASP file "OrderSettings.asp" in a folder called "Orders" - in here we place our initial code for this class:

<%
Class OrderSettings

    Public Property Let CutOffTime(value)
    
    End Property
    
    Public Property Let NextOrderTime(value)
    
    End Property
    
    Public Function CanOrderAt(dateTime)
        CanOrderAt = true
    End Function
End Class
%>

We also have to reference this new location within our unit test ASP file "OrderSettingsTest.asp" with a new include at the top:

<!--#include virtual="Orders/OrderSettings.asp"-->

Now, when we run our unit tests, it still fails, but this time as a result of an assertion and not an exception - we now have somewhere to start with our Red, Green, Refactor TDD process.

First, to get our tests to pass we change the implementation of the CanOrderAt function:

    
    Public Function CanOrderAt(dateTime)
        CanOrderAt = false
    End Function

When we run our unit tests, they pass and we have our first fully functioning unit test on a requirement.

Time for some refactoring and more assertions (from hereon we shall not look to screenshots and take it at face value when a test is described to pass or fail).
We add new assertion code into our unit test ASP file "OrderSettingsTest.asp":

Dim onePm, twoPm
onePm = "10/10/2008 13:00:00"
twoPm = "10/10/2008 14:00:00"

tester.AssertIsFalse currentOrderSettings.CanOrderAt(onePm), "Can Not Order At 13:00"
tester.AssertIsTrue currentOrderSettings.CanOrderAt(twoPm), "Can Order At 14:00"

Our unit tests now fail because we are returning only false from this function, so we change the implementation in our OrderSettings class to account for this:

    Public Function CanOrderAt(dateTime)
        
        dateTime = CDate(dateTime)
        
        Dim twoPm
        twoPm = CDate("10/10/2008 14:00:00")
        
        If dateTime = twoPM Then
            CanOrderAt = true
        Else
            CanOrderAt = false
        End If
    End Function

With this change, our unit tests pass, but we obviously need some refactoring to get this code into a correct state and we logically turn to the properties and want write the code to store those values - we can then use them in our CanOrderAt function. We insert some more assertions, this time for the properties (we might do this one at a time in true TDD, but for brevity we shall do them together here).
The unit test ASP file "OrderSettingsTest.asp" is changed to the following:

<%
dim tester
Set tester = new UnitTester
tester.Init("Testing Order Settings")

Dim currentOrderSettings
Set currentOrderSettings = new OrderSettings

dim cutTime, nextTime
cutTime = "01/01/1999 13:00:00"
nextTime = "01/01/1999 14:00:00"

currentOrderSettings.CutOffTime = cutTime
currentOrderSettings.NextOrderTime = nextTime

tester.AssertAreEqual currentOrderSettings.CutOffTime, CDate(cutTime)
tester.AssertAreEqual currentOrderSettings.NextOrderTime, CDate(nextTime)

Dim onePm, twoPm
onePm = "10/10/2008 13:00:00"
twoPm = "10/10/2008 14:00:00"

tester.AssertIsFalse currentOrderSettings.CanOrderAt(onePm), "Can Not Order At 13:00"
tester.AssertIsTrue currentOrderSettings.CanOrderAt(twoPm), "Can Order At 14:00"
%>

If we run our unit tests again, they blow up with a server error as the properties do not have Get implementations. We add the get code into the file but initially as empty placeholders, then watch our unit tests fail on an assertion exception instead.
We can now proceed with the implementation in our "OrderSettings" class and our unit tests now pass when we run them once again:

Class OrderSettings

    Private m_cutOffTime
    Private m_nextOrderTime

    Public Property Get CutOffTime()
        CutOffTime = m_cutOffTime
    End Property
    Public Property Let CutOffTime(value)
        m_cutOffTime = CDate(value)
    End Property
    
    Public Property Get NextOrderTime()
        NextOrderTime = m_nextOrderTime
    End Property
    Public Property Let NextOrderTime(value)
        m_nextOrderTime = CDate(value)
    End Property

We will take a break here so that this post doesn't become too long - in the next part of this series we will continue to refactor this class and further illustrate test-first development in action using classic ASP.



0 comments: