Automated Unit Testing

Integrating Ruby and Watir with NUnit

Scott Hanselman has been experimenting with Ruby, Watir, and NUnit.  His goal is what all of us web guys are after, a better way to perform unit testing of the web interface.  We have NUnit to test all of our business objects, data acess code, and controller logic but we don’t have any real good options for performing unit testing on the web presentation layer.  Scott developed a WaitrAssert class for integrating Waitr into NUnit which he uses to do all the testing within his application.  Definitely cool stuff…

Automating Unit Testing With a Base Class Posts

I've recently written a series of posts on the process of automating the unit testing of CRUD operations on business objects. 

In future posts I'll dive into some of the details which I didn't go into such as how to set the allowable values for a property, how to ignore a property when comparing objects, how to set a property value as unique, as well as how to manage relationships among objects.  Look for a zip file containing a running example in the next couple of days.

Part 3: Automating Unit Testing with a Base Class

In Part 1 of Automating Unit Testing with a Base Class I provided a brief introduction to Unit Testing, provided an overview of the problems that unit testing business objects present, and briefly discussed why I include the database in my unit tests.  In part 2, I provided an overview of the process I've followed in testing the CRUD operations of my business objects.  In this final installment I'm going to discuss how I've simplified the testing of basic CRUD operations on my business objects by creating a base class for my business object unit tests.

Let's Review

Before moving on to the solution I'd like to quickly review the process I follow in testing the CRUD operations on my business objects.

Create

  •  Instantiate an instance of the object
  • Set the properties of the object with valid values.
  • Call the Save() method on the object, checking that the save succeeds.

Read

  • Do Create.
  • Retrieve the object out of the data store and check that the values of the saved object equal the values pulled from the data store.

Update

  • Do Read
  • Change the properties of the object.
  • Call the Save() method on the object.
  • Retrieve the object out of the data store and check that the values of the updated object equal the values pulled from the data store.

Delete

  • Do Create
  • Call Delete() on the object.
  • Try reading the object back out of the data store and ensure we get a null object (since its deleted).

See Part 2 for the code representation of the above

What can we Automate?

Over the past year I've written a lot of unit tests for the CRUD operations on my business objects.  A couple months ago I was working on a moderately sized .NET project coding away, writing failing tests for each of my CRUD operations, writing the code to make the tests past, and refactoring my way to cleaner code.  The process was feeling pretty good, except for one thing.  I seemed to be writing the same code over and over.  Duplication is bad, so why do I allow myself to write 50 test classes with almost identical logic for testing the CRUD operations on my objects?  I try to follow the Don't Repeat Yourself principle that Dave Thomas and Andy Hunt present in The Pragmatic Programmer so I set out to find a better way.

As I began my journey I began to evaluate the tests that I was writing for my business objects.  The tests were slightly different across the project but I saw a similarity among them that led me down an interesting path.  Each test object was essentially performing 3 different tasks, the only difference between the tests was the objects that they were performing the tasks on.

  • Task 1 - Loading objects with data.
  • Task 2 - Calling methods on my business objects (Save, Delete, Load).
  • Task 3 - Comparing two business objects to ensure the values saved and the values loaded were identical.

Task 1 - Loading Objects with data

The first task I identified was the process of loading objects with data.  In order to test the CRUD operations on my objects I first needed to load the objects with data so they could be saved to the database.  Let's take a quick look at how the data was loaded in the unit tests outlined in Part 2 of this series:

Customer customer = new Customer();
customer.ContactName = "Steve Eichert";
customer.Address = "221 South North West Ave.";
customer.City = "Philadelphia";
customer.Region = "PA";

To load the Customer object with data I instantiate an instance of the Customer class and then set the properties of the customer.  The class being instantiated and the properties being set change for each unit test, however, the process is the same.  First instantiate an instance of the class and then set the properties of that class.  Now that the task has been identified how can it be automated?

Provided we have the type of the business object we can use Activator.CreateInstance to instantiate an instance of the class.

BusinessObject bizObject = (BusinessObject) Activator.CreateInstance(BusinessObjectType);

After an instance of the class is created we need a method for setting the properties of the object.  The System.Reflection namespace gives us just what we need.  By using the GetProperties() method on the System.Type object we can identify all the properties defined on our objects.

///

<summary>
/// Load the properties of an BusinessObject with random values.
/// </summary>
/// <param name="bizObject">The <c>BusinessObject</c> to load the properties of.</param>
public
void
LoadProperties(BusinessObject bizObject) {
   System.Reflection.PropertyInfo[] properties = BusinessObjectType.GetProperties();
    foreach(PropertyInfo property in properties) {
      if(property.CanWrite) {
      
SetDynamicPropertyValue(bizObject, property);
     }
   }
}

The LoadProperties() method retrieves all the properties available on our business object and then loops over all the properties and sets appropriate values (SetDynamicPropertyValue).  Depending on the type (string, int, bool, etc) of the property we need to set a different value.  We also need to consider that certain properties have a limited set of potential values.  To set the value of each property we need to first determine the type of property using the PropertyType property of the PropertyInfo object (say that 10 times fast).  The below block of code shows how this can be accomplished.

///

<summary>
/// Set the property of an <c>BusinessObject</c> to a random (dynamic) value.
/// </summary>
///