2007-02-14

Unit Testing: still rewarding

I'm not going into all of the obvious virtues of unit testing. If you're a developer and you don't know them, you should probably find a new career.

I didn't like unit testing. I didn't like spending my time writing a test when there was plenty of useful code to be written. Working at Capital Blue Cross for a year changed all of that. My partner, Andy H., insisted that I write a unit test for every part of the system. My work wasn't considered done until the corresponding unit test was written. What a taskmaster. Consequently, I didn't feel productive at all.

Near the end of the project, it was time for us to start running the application through its end-to-end testing. After a few runtime setup environment problems and false starts, the application ran without a problem. It was one of those "Did it just run without a problem?" moments of disbelief. Not only did it run without crashing, it produced the output we were expecting. Hopefully Andy will back me up with a comment so my readers know that I'm not making this up.

OK, so unit testing produces applications with less bugs. Duh. If you didn't know that, or worse, you don't believe it, please stop reading now.

Now for the benefits that I wasn't expecting.

Writing testable code means writing readable and organized code. For example, I wrote an object responsible for building an email using contextual data and a template and sending it. I blew through the implementation pretty quickly. In no time, I had a 150 line method that did it all. OK, time to test. Congratulations jackass, you just wrote a lump of untestable code. I was forced to break the huge-ass method into smaller, more testable, methods. When I was done, I had a class that was easier to read. Even better, I had a class that was tested.

No more Winnebago classes. We've all done it. "Oooh, I know, I'll add this neat feature. No one needs it right now, but someone might need it in the future." When writing code with unit testing in mind, you tend to skip those neat-o features when you also have to write a test for them. The result is lean code that does only what it has to do.

Writing unit tests forces you to think about the design from a different perspective. You may write a nicely organized class that is easy to test, only to realize a design flaw while writing the unit test. After writing a unit test with 70 test methods, you may think the class would make more sense if it were broken into two classes.

Even though I've had mostly positive experiences incorporating unit testing into my development cycle, I still feel unproductive while I'm writing a unit test. Hopefully this feeling will change. I guess I feel unproductive because the benefits are delayed. I take great pleasure in watching 16 unit tests fail because a developer (probably me) made a "simple change" to a core class.

9 comments:

  1. Unit tests are great for proving that you've done something right, but they don't prove that you've done the right thing. So they are more helpful in environments with mission-stability and implementational-instability (e.g. new code for a big insurance company) than in environments with mission-instability (I'm sure you can think of an example here :-)).

    Also, unit testing doesn't catch integration errors. With large, distributed, highly interconnected software projects, integration errors tend to dominate implementation errors. Sometimes that "simple change" to a core class is truely needed, so taking pleasure in watching 16 unit tests fail is like taking pleasure in having a stagnant mission.

    I guess what I'm saying is: Unit tests are useful for what they are useful for (internal consistency checks of implementations.) But internal consistency should never be allowed to dominate external needs, else your have shackled yourself to a specific implementation.

    In other words: Use unit tests to verify, but not to validate.

    ReplyDelete
  2. JC, I have a few comments:
    - Yes, I still read your blog.
    - I was as very pleased when the system tests ran and gave us the expected results so early in the process. The unit test are not always popular but they did exactly what they where intended to do, allow us to test a small piece of code with a limited amount of input.

    ReplyDelete
  3. "...Sometimes that “simple change” to a core class is truely [sic] needed, so taking pleasure in watching 16 unit tests fail is like taking pleasure in having a stagnant mission."

    Simple changes are always needed. You're missing my point. I take pleasure in watching 16 unit tests fail, because I was only expecting 4 to fail. When it happens I think -- Wow, I never would have thought that simple change would affect (effect?) so much code. Thank God (ha ha) for unit tests."

    Here's surprise -- I disagree with you. I think unit tests are useful on any project, mission-stable, implementation-instable, mission-instable, or whatever. To think any developer would ever allow a unit test to shackle an implementation is silly to me.

    ReplyDelete
  4. Thanks Andy. How would you like the payment? I can do Direct-deposit, PayPal transfer, or unmarked $20 bills with non-sequential serial numbers.

    ReplyDelete
  5. Like this blog entry.

    ReplyDelete
  6. I wrote a unit test once. It was boring.

    ReplyDelete
  7. Sweet Tea, you complete me.

    ReplyDelete
  8. The phrasing I used was "They are more helpful..." I included the word "more" by design, because I agree: Unit tests are always helpful. They help prove the internal consistency of your code, which is A Good Thing. My objection only happens when unit tests are used beyond their limitations... when they are used to say "our design is the right design" instead of simply "our implementation does what it says it does."

    And thanks for clarifying that you were happy the test showed 16 errors instead of 4. I read it as "the unit tests brokes, so clearly the change to the base class was wrong" (which I thought was a bit of a leap.)

    ReplyDelete
  9. Geeks fighting. This is a real turnon.

    ReplyDelete