This past week, while working on a couple updates to some existing code I had to update the corresponding unit tests to ensure the stuff I added didn't cause massive problems. As I started exploring the unit tests for the existing functionality my head started to spin. Either I was going nuts, or the tests were less than ideal. After examining the code for a little while I started to see why my brain wasn't working so well when it came to examining this particular chunk of code. While the tests were testing the functionlality that they needed to, they weren't nearly as readable as they could have been. Some of this is a result of attempting to reduce the amount of duplication within the test class. You see, several things needed to assert the same things, so they were seperated out into a method that you could pass some stuff to, and it would magically assert everything that you could ever want asserted upon the so said "stuff". While nice in concept, it led to code that was very hard to follow. In order to see what an individual test was doing I needed to jump around to 3 or 4 methods that contained the asserts. Once I finally tracked down the asserts that were being used I was able to see what was expected. It just so happens that I could also see that we were testing the same thing 3 or 4 different times in an individual test. For good measure, we also appeared to test the same exact logic in multiple tests! All of this has led me to the following conclusions:
- You should prefer readability over well factored code for unit tests. If you need to jump all around to figure out what you're testing something's wrong. If duplication helps with readability, duplicate away!
- Make sure your unit tests are only testing one thing.
- Make sure you don't re-test the same thing over and over and over again. Taken with the above point, you should begin to see that you shouldn't have that many tests that need to assert the same thing, if you do, you probably have multiple tests testing the same thing.
- Only tests one class per unit test class. If you're testing ClassA and it leverages logic within ClassB and ClassC, don't test ClassB and ClassC's logic within your test for ClassA. Create seperate classes for ClassB and ClassC. Trying to test them all in one place will lead to unit tests that are too hard to read and doing too much. This is particularly hard to keep in mind since often times as part of the process of doing test driven development ClassB and ClassC might be created. It's tempting to leave the existing tests that test the logic that used to be in ClassA but has since been moved to ClassB and ClassC within ClassA's tests. Don't do it, move the tests to the suite of tests that you're creating for ClassB and ClassC. You'll thank yourself when you come back to change that logic, since it will be where you expect, rather than in a test class for a related class.
I've gotten distracted by Lilo and Stitch so I'm going to stop with my conclusions there, and go play some indoor basketball with my son who is throwing a basketball all over our house. :)
[Disclaimer: It should be noted that I'm pretty sure the code I was looking at was code I wrote...whoops :(]