Over the weekend, I inherited some unit test code. This code was functional, but it was my job to inspect it to ensure that we had appropriate coverage. It covered a pretty broad spectrum, so after 10 hours of inspecting the code carefully I had come up with some observations that later led me to these handy unit testing guidelines.
When designing unit tests, ask yourself the following questions:
- Is my test setup concise, clear, and flexible?
- At a glance, is it clear what this unit test is actually testing?
- Am I asserting the proper results apply for all relevant fields?
Let’s look at these questions in further detail to understand better:
Is my test setup concise, clear, and flexible?
The root of this question lies in lazy coding versus intentional coding. The code that I happened to be testing relied heavily on certain classes and did not have an overloaded constructor. This forced what should have been a 1-2 line constructor to build the object into a 10-15 line long block of setting arguments for the class after it had been created. To make matters worse, most of the code was using the same parameters each time the object was created, which just begged for refactoring. We spend a lot of time in our unit test code, in some cases MORE time than our actual production code (especially if you are practicing Test Driven Development) so you owe it to yourself to write concise, clean, and readable code. If not for you, then for the next poor schmo that has to read your code later.
By ensuring you have proper constructors and helper methods to make your default objects and allow setting of arguments, you don’t pull focus from what the unit test is actually doing by hiding it’s value in tens to hundreds of lines of test setup. In practice, I find myself questioning if my setup is concise enough if it extends much beyond 10 lines of preamble before I call the method I am testing. This leads nicely into my second point:
At a glance, is it clear what my unit test is actually testing?
Unit tests should either test a specific method’s output, or a specific business scenario. In both cases, you don’t want the actual method you are testing to be hidden amidst tens to hundreds of lines of setup. If you have that much setup, you need a helper class to enable quick setup of the objects and values that you need to test your method especially if you’re planning on doing more than one test or your QA team hasn’t gotten hold of the feature yet. (I could go on a rant here about test driven development and why it is important, but that topic is for another time).
Am I asserting the proper results apply for all relevant fields?
Last, but certainly not least, it is important never to lose sight of what a unit test is meant to do: assert, assert, assert. If you are testing an object, it does not hurt to assert each value that is relevant to the method that you are testing. Often times, it is easy to assume that another unit test somewhere else in the codebase is testing that field but I have found that often there is less coverage than expected. Spend the extra few lines and assert all of the return values in an object, especially if the case took a considerable amount of setup code to get rolling.
In this same vein, also be careful to think about scenarios how your code will be invoked or used. Often, unit tests do a good job of testing the individual puzzle pieces that make up your implementation, but sometimes if fit together incorrectly the feature still does not work properly. In our group, I recommend a combination of traditional unit level tests with more “functional units” that exercise the workflow to ensure that the piece of code I am testing plays well with the other bits of code around it. A good example is testing a method directly and then testing the client service that wraps it into a feature to ensure that the proper values are returned in both cases.
So that’s it! Three simple steps that help you toward writing better unit tests. If you find this type of thing interesting, let me know in the comments and I will write more!
Related Articles
No user responded in this post