Sunday, May 27, 2007

TDD, What a bitch

Whenever I talk to someone who is interested in learning TDD I let them know that it is a very difficult concept to put into practice. It takes a lot of discipline and significant changes in the way you think and code.

Nobody mentioned this to me before I started. Everyone spoke of TDD like magic -- it changes your design, its the best thing ever, its so simple, all you need to do is write your tests before your class, you'll love it... (Ignore the man behind the curtain!)

After a few days of trying, I began to think something was very wrong. I wasn't quite the developing genius I thought...

That couldn't be true!

Like any reasonable person in my situation -- overstressed, too many responsibilities, tight deadlines, stakeholders bothering you, etc... I continued to pound my head against what seemed to be a steel wall of TDD that just wouldn't fit.

My head (now bleeding and bruised) was focused on how to solve the problem instead of how to test the solution. I didn't know it at the time, but until I changed this behavior, I would always be fighting my instincts and not gaining the benefits.

Fast forward two months and I was still, well, sucking, but not as bad. I had some test coverage, I wrote some classes test first, but I did not test drive every class, it seemed like too much work. I also didn't understand mocks, at all, and gave up on writing my own mock classes after about a day, so no interaction testing and lots of coupling.

It was very frustrating to have 1/3 of my tests fail if I was working on a common entity or if a bug showed up. Also, because I was coupling all my classes together I wasn't seeing the full benefits of test driving.

My focus was just as bad as coding before. If I needed a new behavior, I'd have to code. This meant testing the other class, which may cascade into other tests. Thinking about it, I think my focus was worse. Sometimes I could write a stub for the new behavior, but say I was creating a method that returned a boolean that had different responses that I needed to test... And there is the setup to get it into each state. And thats just a boolean assuming there's no interactions...

My thinking was in the same place it was a few months earlier. The only difference was I could write a unit test before I wrote code. But I wasn't test driving design. I had the design in my head. I was writing the unit tests for the classes and interactions I already decided I needed.

Fast forward two months: New job, new project, fresh start, woo hoo!

With this project, I was determined to understand TDD. A lot of bright people that I respected were saying great stuff and I was going to get it.

My first step was to understand nMock.

I knew using real classes in my tests was killing my focus, but I didn't yet know how much it affected my design. I read the nMock web page, explaining the api, about 500 times... "Okay," I said to myself. "You can use an interface before its implemented and verify its been called."

It sounded so simple, it read so simple, yet it made NO sense to me.

My delight in using mocks (once I figured out how to use them) was focused on several things -- I could focus on the test I was writing, my tests weren't failing because of another class and my tests were a lot simpler because I didn't need to put in knowledge about other classes to verify or assert behavior in the CUT. Implementations of interfaces used in tests didn't affect the tests anymore.

After a few weeks of nMock, what I began to notice was that my design, code, thinking, and mind was changing.

A lot.

I only made a class after it was being used as an interface in another class. That class' only public methods were in that interface. I never debugged my application. I almost never debugged my tests. My test coverage was over 90%. My design evolved through the tests.

I was test driving. Finally!

Making tests was so focused and had immediate results I was addicted. At some point, something happened in my brain and this great thing called TDD finally came in. I didn't want to write in the CUT first, because it wasn't how I was thinking. It seemed like more work.

I was in the test.


Dave said...

Heh, funny title and so true!

hammett said...

Isn't there a typo on the title? Or is it part of a joke?

wunda said...

hammett, you caught me again :)

I bet you won't find any mislpellinsg in my tuping everr agin!

Robert said...

Great post. That's why you are such a excellent TDD teacher -- you've really been through it. I still have a long way to go till I "get it" (as the EST people used to say). Thanks for all your help along the way. PS it's steel wall

Justice~! said...

Congrats on "getting it". I had similar experiences when I was learning it and I've viewed lots of people go through the same thing when I've been teaching it. It's like any other breakthrough though - once you get it it makes all the trying worthwhile.

wunda said...

robert, thanks! You're a lot closer than you think :)

wunda said...

Justice, I find more people are openly admitting they had a tough time learning TDD now than a few years ago, which is great for people trying to learn. Thanks for adding your feedback, though I think you may have done so just to tell Donald we had a nice chat this weekend! hahaha

AJ said...

Much appreciated article, this couldn't be more timely. Still trying to get into the TEST and not the SOLUTION though. Of course, I'm only 1 month into TDD....

Bill Campbell said...

Great Post!

This is so me (the first few paragraphs and months). I'm struggling. It's just not 'there'. And I don't know how to make it clear. I attended the most awesome Nothing But .NET week with JP and he blew my mind! It all seems so easy when you watch someone do TDD. It all makes so much sense. Then you take the most simple problem and it's immediately lost.

But your words of 'crossing over' are a blessing to my ears. It's just so painful in the meantime! :)

Thanks for a great article and keep on writing and telling us about it!

Kenneth Kasajian said...

Back in the early 90s, many programmers who had been developers who used structured programming resisted moving to object-oriented programming. There were a lot of reasons why they didn't want to use C++, and stick with C -- anywhere from performance to not wanting strong-typing.

The bottom line was, it was difficult for a programmer to change after solved problems a certain way 10+ years. "Hey, I've solved this problem a 100 times, and I did it in C"

Thinking in "objects" was a very difficult transformation, and many developers didn't have there "aha" moment until years after they were actually practicing it by going through the motions.

Chats amongst these developers were often interesting. You'd hear things like, "You know what's odd, my 12 year just 'got' objects from the beginning, and it just seems natural to him", with a response from another, "I think objects are supposed to be more natural". Wow.

Moving from non-TDD programming to TDD has at least one similarity to the above in that, programmers who aren't interested often think, "Look, I've written a lot of code without TDD, and besides, we have QA to do tests".

Like Wendy said, TDD is very very very difficult to start doing because your brain just wants to do it the old way.

But it really isn't anything different than any other habit.