Sunday, January 18, 2009

On testing data objects, the first step towards TDD

On testing data objects, the first step towards TDD

The simplest class behavior to test is the basic mapping of an object's properties either through it's constructors or through it's accessors. This translates to validating the correctness of the object creation, or validating the correctness of the object's modification.

Validating object construction

The first and easiest test cases involve the construction of an object. Usually the first test case involves validating and object's default values through a default constructor. Remember to question what an accessor should return if an object field is null.

Subsequent tests involve and validate that the construction parameters are correctly represented through the objects accessors.

I encourage validating a single object property per test. This makes it easier to identify what code is at fault when a test fails, it also facilitates defining test names which describe the expected result of the test.

Validating object modification

The next step in object testing, may well be the modification of properties. I am personally of the opinion that this should cause us to question our object modeling, but there are some valid uses for this, see bellow.

These tests involve constructing an object with one set of values, and validating that modification to that truely re-assigns the value. It is important to use test data that clearly demonstrates the modification of the field. So if the initial value is the value '1', the modification should assign '2'.

There is little interest in validating multiple subsequent modifications on a specific object instance unless the object supports some special caching or history behavior. In most cases and assignment is an assignment is an assignment. Dont clutter your tests with sequential validations.

Why validate the obvious case?

In the javabean and pre-version 3.0 C# approach to properties, an accessor is an explicit block of code. Consequently, it can contain bugs. This is rarely the case, but it does happen from time to time. Usually such bugs are quickly identified and fixed, and subsequently not repeated, but there are nonetheless reasons to validate them.

Remember that even if the property uses the trivial implementation, it remains a kind of method. Even C#'s properties are a kind of field-separated accessor with a signature that remains independent of the internal class data representation. This means that through the magic of refactoring, changes to the underlying structure of a class can continue to expose identical properties even if the origin of said properties has changed. Wouldnt it be nice if we could validate that a refactoring of a class' internal workings continued to provide the same external behavior? I certainly think so.

In C# 3.0, it is now possible to specify a property with a short-hand notation which automatically generates the trivial implementation. This does not invalidate my previous argument. The short-form syntax can be replaced with an elaborate evaluation logic without changing the class' signature. A simple test could help to catch a sudden change in behavior. Remember that tests are a kind of code-level contract. Changes in class behavior are a change in contract that must be re-negotiated by the programmer for all the affected parties including the test code.

How far should we go with testing the trivial implementation? Not very far. The tests are just a safe-guard against obvious violations, and the number of tests should reflect the complexity of the tested code. Only if the underling code grows in complexity, should the tests grow in number.

What about encapsulation?

Let me underline that this discussion focuses on data objects. Objects who's primary function is to store and transport data. Though there are reasons and contexts in which the are appropriate, and others in which they can be avoided, they remain the most frequent type of object that I've seen in application code bases. Regardless of your opinion on object modeling, they remain the easiest kind of object to test, and so for a TDD newbie, they are the low-hanging fruit.

Conclusion

Data objects are easy to test. The simplicity of their implementations make them an ideal starting point for test-driven development newbies. Start by validating object construction, then move on to object modification.