KevinUp

real world programming

TDD

Posted by kevinup on May 15, 2007


Test driven development. Is there any other way to develop? I was first exposed to this kind of development on my current project about a year ago by Dave Donaldson. While I don’t think that I’ll go into all the points of TDD, but let’s just say if you aren’t doing it, you suck. I remember dave would go through our code base looking for optimizations, problems, basically general code auditing. And our testing framework would make sure that his refactors wouldn’t break anything. Something I noticed was about 20% of our test were something like this:

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void AddCustomer_NullExceptionTest()
{
    CustomerManager.AddCustomer(null);
}

Here is what is being tested.

public static long AddCustomer(Customer customer)
{
    if (customer == null)
    {
        throw new ArgumentNullException("customer", "Customer cannot be null");
    }
    //rest of add logic code...
}


Back to the point, now every time you write a new public static method, and are writing tests, you always have to write a negative test to get your code coverage up. Here’s the thing about negative tests, they are always the same. So I thought it would be cool if I write a test that passes null into every public static function, and verify it is either throwing an expected exception, or not throwing an exception at all. So now we down the number of tests we have to write by 20%, and we put it into 1 test. Here is my test (Modified slightly for this post):

[TestMethod]
public void NullCheckTests()
{
    Assembly assembly = Assembly.Load("Target.Assembly");
    Type[] assemblyTypes = assembly.GetTypes();
    for (int counter = 0; counter < assemblyTypes.Length; counter++)
    {
        if (assemblyTypes[counter].IsPublic)
        {
            MethodInfo[] methods = assemblyTypes[counter].GetMethods(BindingFlags.Public | BindingFlags.Static);
            for (int i = 0; i < methods.Length; i++)
            {
                if (methods[i].GetParameters().Length > 0)
                {
                    List<object> parms = new List<object>();
                    int para = methods[i].GetParameters().Length;
                    foreach (ParameterInfo p in methods[i].GetParameters())
                        parms.Add(null);
                    try
                    {
                        methods[i].Invoke(null, parms.ToArray());
                    }
                    catch (Exception ex)
                    {
                        if ((ex.InnerException is ArgumentNullException) ||
                        (ex.InnerException is ArgumentException) ||
                        (ex.InnerException is InvalidOperationException))
                        { /*everything worked*/}
                        else
                            throw ex.InnerException;
                    }
                }
            }
        }
    }
}

Even if your methods take value types, like ints, longs or enums, it still will pass in the default value. When I first ran this test, I found a lot of tests that weren’t throwing the correct exception, weren’t checking exceptions at all, or were throwing the exception I hate the most, NullReferenceException. Initially out of around 900 public tests in our testing framework. Only around 500 were passing this test. It took a while, but now I’ve got this test passing. Now developers don’t have to write ‘negative’ tests. And there is a test making sure that they are handling negative input. I know there is an FxCop rule that checks for this, but now, we can get some coverage out of it.

Advertisements

One Response to “TDD”

  1. “…but let’s just say if you aren’t doing it, you suck.”

    Freakin hilarious.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: