Unit Testing Roadblocks
August 1st, 2008Unit testing is a good idea. Ask any random developer what they think about unit testing and you’re likely to get one of four responses:
- I don’t know what they are.
- I love them! You should always use them!
- I hate them! You should never use them!
- I appreciate them, but I’m lazy and don’t use them that much.
Of these, I have the most respect for the first and fourth. If you don’t know what unit testing is, you can hardly be blamed for failing to employ them. Go learn more about them and maybe you’ll enjoy the rest of this entry a bit more. If you are familiar with them but feel that you don’t use them enough, don’t beat yourself up. You’re probably using them “about the right amount.” That is to say, as much as you need to, in order to alleviate your pains.
Groups 2 and 3 could practically be folded into one, because each is about as useless the other. Some things in life demand a strict behavioral code, but programming is not one of them. Some kinds of code bases and programming tasks are extremely conducive to unit testing, and others are not. Use them wherever they make sense, and where they accelerate your ability to improve upon your existing code base. I won’t go into much more about the whens and ifs, but Wil Shipley and Bill Bumgarner have delved deeper into this question, if you’re interested in reading more.
Laziness Incubators
Let’s assume for the sake of argument that you’ve learned about unit testing, and self-disqualified yourself from either of the extremist groups described above. You probably find yourself in that fuzzy group that I’ve labeled lazy yet appreciative. Laziness on its own is one thing, but it doesn’t help when tedious roadblocks are put in our way to incubate further laziness.
“I should really add a unit test suite for this complicated class.”
…
“But then I’d have to create a new unit test file, and stuff…”
I’m ashamed to admit that this is how many a unit test has been put off. Other laziness incubators include the friction of adding a suitable unit test bundle target to a project, and the difficulty of deciding how to factor your unit tests so that they make sense in the context of your project. Oh, and let’s not overlook the difficulty of having to think up how to write the specific unit tests themselves.
Apple makes some of these roadblocks somewhat easier, with built-in unit testing support for Xcode. Unfortunately, they only go about 80% of the way in many regards, leaving us floundering to figure out how to fill the gaps for the remaining assistance we might need. These gaps where we’re forced to scramble for solutions can be a great deterrent to using unit tests as much as we might like.
Debugging Unit Tests
So you’ve overcome inertia and established unit testing build targets in your projects. You came up with an approach that makes sense for your code, created a new unit test suite source file, and actually written a few tests for your code. But now you’re bound to run into a vexing question: “how the heck do I debug this thing?”. Since unit tests are generally built into a standalone bundle, there’s nothing for Xcode to run. But when you come across a failing unit test and you can’t figure out why, you find yourself wishing you could step through the code just as you might in an application.
The answer to this question, as well as many other “missing pieces” for unit test support in Xcode, comes from Chris Hanson, who shares a great deal of useful information on his blog, including this gem on debugging unit tests. This post also contains a number of useful links if you’re still struggling with the other roadblocks such as adding a target, etc.
What we learn from Chris is essentially that the easiest way to debug unit tests in your project is to set up some executable, any executable, and arrange for your unit test bundle to be “injected” into the executable at runtime. Now, to debug your unit tests, you just ask Xcode to debug the injected executable.
It turns out this isn’t very difficult, but it’s tedious. To set up this custom executable, you need to add a bunch of cryptic command line arguments and environment variables to your project. Yuck! This cumbersome process has caused me on many occasions to “just skip doing unit tests for now.” That’s not really helpful to me or to the health of my code, so I finally got off by butt and wrote a handy AppleScript to do it for me:
Download Add Unit Test Executable. Free AppleScript.
What does this script do? In a nutshell, it asks for the name of your unit test bundle. Provide it, without the “.octest” extention, and the script will add a custom executable to your application, with all the gross arguments and environment variables needed to “make it work” in both Xcode 3 and Xcode 3.1. I added it to my Xcode application-specific scripts folder, and select it (using FastScripts) whenever I find myself in a project that doesn’t already have a suitable custom executable for debugging unit tests.
My script uses TextEdit.app as the test harness application, because it’s on everybody’s Mac, and because it seems like a fairly innocuous host application. Of course, you could use your own application, or another custom host application of your choosing. Just edit the script to suit your needs.
Hope this helps some of you overcome one source of laziness getting in the way of writing excellent unit tests!