What To Unit Test

Several months back I wrote about unit testing.  Following that I received a question from a reader about how to actually carry out writing unit tests.  What should be tested?  How much is enough?  There is no single answer to these questions but I can give guidance.  The actual answer depends on the specifics of what you are trying to test and how much time you have to devote to unit testing.  What follows is a list of the items you should test, in the order you should test them.  If you only have limited time, start at the top of the list and work down until you run out of time.

In each case below, you should begin by writing tests for the positive cases and move on to the negative ones.  By positive cases I mean those things you expect a user to do.  This is the way the feature will be used if it is being used properly.  Call each function with all the equivalence classes of data that they would be expected to see in normal circumstances.  Ensure that the behavior you observe is what you expect.  This means actually verifying that the right behavior took place.  If short on time, you can often use the interface to verify itself.  Calling a Get after a Set to verify that the value was set correctly is a good example of this.  Even better is verifying through some alternative mechanism that the right behavior took place.  This could be looking at internal structures, observing behavior, querying a database, etc.

Negative cases might also be called bad data.  These are the inputs you don't expect.  This includes out of range values, invalid objects, null pointers, improperly initialized structures, etc.  Once upon a time we could ignore these and consider the result of these inputs to be undefined.  In the days of networked computers and the security implications now present in many flaws, they cannot be treated lightly.  It is important to test for these and ensure that the proper behavior is observed.  That behavior could be to ignore the data, return an error, raise and exception, or even crash safely.  The important thing is to understand what the behavior should be and to verify that it indeed takes place.

The first and most important thing to hit is the point where your customers will interact with your code.  This could be an API or a UI* or a web service.  This is the highest level of abstraction and perhaps not the easiest or even the best place for unit tests, but if you only have a little time, this is where you'll get the most bang for your buck. 

The next place to go testing is at the internal interface level.  This means testing the places where the internal objects/functional blocks interact.  If you are programming OO, this means testing all of the public (or friend) interfaces on all of your objects.

Finally, test unit test all of your internal utility functions and private object methods.  Crack the abstraction layer on your objects and probe their internal functionality.

If you do all of this, you'll have a pretty good idea that your feature is functioning as desired and will have warning when something breaks.  Remember to run all of the (relevant) tests before *every* checkin.  Also, be vigilant.  Any failures at all should be fixed before checking in.  It is not okay to ignore a failure.  If it is a false failure, fix the test.  If it is a valid one, fix the code.

 

* You probably cannot unit test the API but if you properly abstract the actual UI, you can write unit tests for the functionality of each UI element.