Saturday, September 23, 2006

Data Driven Testing - Part 1

It's 3am, you've been working all night on a new feature, and you're about to commit your code for QA testing; you ask yourself is this going to work?

We've all been there. No one likes their work being rejected, or being known as the developer that commits faulty code. Worse yet, the bug sneaks past the testers and is discovered months after it has been in production.

So, how do we prove our code works before we commit it into the next build? I hope you all said Unit Tests. There are many different unit testing patterns outlined at XUnit Test Patterns. This post focuses on how to maximize the test coverage of your data dependent .NET components with minimal code. Sound good? Then lets get to it.

You may be asking yourself, what is a data dependent component? In short, it is a component that takes a finite set of input values and has produces a discrete output based on those values. In the example below, we have a postal address class that has complex validation rules (region based on the country, postal/zip code based on country, etc.). I am assuming you are all familiar with NUnit; I have recently started using the MBUnit test framework as it is a lot easier to test data dependent components.

[RowTest]
[Row( "1234 Some Street", "Vancouver", "V9V 9V9", "BC", "CA", true )]
[Row( "1234 Some Street", "Vancouver", "V9V 9V9", "BC", "US", false )]
[Row( "", "Vancouver", "V9V 9V9", "BC", "CA", false )]
[Row( "1111 Rich Drive", "Beverly Hills", "90210", "CA", "US", true )]
[Row( "1234 Some Street", "Vancouver", "90210", "BC", "CA", false)]
public void TestPostalAddressValidation( string streetAddress, string city,
string postalZipCode, string region,
string country, bool isValid )
{
PostalAddress address = new PostalAddress();
address.StreetNo = streetAddress;
address.StreetAddress = streetAddress;
address.City = city;
address.PostalZipCode = postalZipCode;
address.Region = region;
address.Country = country;

ValidationResult result = address.Validate();
Assert.AreEqual( isValid, result.IsValid, "The Error Message is {0}", result.Message );
}
The above code snippet demonstrates how you can abstract your data input from your test code. As you can see, we expect our postal address validation to catch the following problems:
  • a region that does not belong to the given country
  • a postal/zip code that does not match the appropriate format for the given country
  • missing data
Although this example may seem trivial, I wanted to start with a gentle example. You may be thinking - I'd have to setup hundreds of Row attributes to cover all of the possible validation combinations. In my next post, I will show you how to handle this in a much more efficient manner.

If there are any data driven testing topics you'd like to see in a future post, please let me know.