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.