By Peter Bell

Why Test?

First in a series on Unit Testing and TDD.

All of the great programmers I know design their applications using Test Driven Development. Most of the other programmers I know have plenty of excuses why they don't (myself included). What's so great about Unit Testing and what are the benefits of TDD that make it so ubiquitous amongst the smartest programmers?

Firstly, unit testing. I find that if I don't write unit tests, as my application starts to get to a certain size, I lose trust in the code. In a complex end-client application I end up with areas of "don't go there" code which I don't want to touch as I know it works, but I'm not quite sure what would break if I change it. In my in-house framework and generation code I find that I have really cool automated code for speeding up tasks, but I often don't trust the corners and the edges of the automated code that I seldom use so if I run into a problem with them I just hack together a custom method rather than taking the time to make my core code more solid (as I'm usually on a deadline and it's almost always expressed in hours or minutes - not days or weeks). I'd really like to be able to build larger applications with confidence, but to do so I need to take the time to build it with components where I've worked through all of the corner and edge cases I can think of and have created an automated test suite so whenever I refactor my code I can see immediately whether it has broken any of the "contracts" that I depend upon in terms of my expected behavior for the code.

Unit tests can also be a great source of agile documentation. They are agile because they are very DRY - you are writing an executable test and just naming it appropriately to make clear the behavior that the test is checking for (and hence the behavior that your class supports assuming that the test is passing). There is no worry of the documentation getting out of synch with the code because if it does, the tests will stop passing and you'll have to rewrite them to express your new intent. And if you take a test driven approach to development, you'll have documentation of the intent of your new class files just before you code them.

Test Driven Development has some more subtle, but more profound benefits. With TDD, you write a test and then the simplest possible code to make that work - adding additional tests and the simplest code required to make those work until your tests cover all of the functions that your class files need to perform. After writing passing code for each test there is a "refactor" step where you clean up the implementation of the code (including the test code) to make it DRY'er and more maintainable hence the common "Red-Green-Refactor" description of TDD (write a failing test, write the simplest code required to pass the test, then refactor the code). It's important to realize that TDD isn't about testing. It is a different way of designing the details of an application. Not using TDD, I've noticed that it is often easy to write over complicated code or even to get stuck in "analysis paralysis" - trying to figure out how to write the one true API that will forever meet your applications needs! With TDD you work iteratively by adding a new requirement (a test) and then simply writing the simplest code that will pass that test. I'm looking forward to trying it on a real project as I've heard pretty much nothing but good things about the experience.

Stay tuned for more postings!

Comments
@Peter,

"All of the great programmers I know design their applications using Test Driven Development"...

Oh snap :) So it's like that?? .... just kidding.

I am looking forward to your exploration of this. Are you doing to stick to just CFC type testing, or are you going to be including interface TDD using something like Selenium? I can sort of wrap my head around the usefulness of CFC-TDD, but the idea of creating interface oriented test driven development is waaaay too crazy for me to wrap my head around. CFCs are so finite in their touching; on the other hand, the number of ways in which you can touch an interface seems infinite. And, if you have any sort of linked select boxes or AJAX style interfaces .... it hurts my head just to think about.

I am also curious to see if, like frameworks, there is a balance between usefulness and application size / team size. I know that no one plans for their apps to stay small forever, but I wonder at what point does this seem overkill.

Looking forward to your analysis.
# Posted By Ben Nadel | 4/16/08 11:21 AM
I don't want to kill your enthusiasm, but be prepared for a tough road on some testing, especially as part of your first real project. I say it not really for your sake, but for the sake of others who might try it out and ditch it before giving it a full chance.

The concept is quite easy. The practice can be difficult, because its hard to know what to test or how to test it when you start using it in practice. It gets easier with time.
# Posted By Sammy Larbi | 4/16/08 11:23 AM
@Ben, LOL :-> Right now, my focus is on TDD for my class files. I also want to add functional testing using BDD but I'm gonna have to port rspec to CF, so that won't be this week :-> Then I also want to add the selenium acceptance testing and eventually some load testing, but that may take a while.

Yep - there is always a balance. For a really simple app, I put the query a the top of the page and the display at the bottom - no OO required. I can create fairly simple OO apps without tests, but once I start having problems with edge cases or testing or am scared to change the code, I know that was a project I should have done test first. Its a no brainer for my framework, but the goal is to create a structure that makes test creation and running so easy I just do it for any class files I manually code. I'll be baking this into both LightBase and my deployment automation software so I can TDD everything that isn't just plain trivial.
# Posted By Peter Bell | 4/16/08 11:59 AM
@Sam, Agreed. I've spent a lot of time looking into this and issues, appropriate use of mocks, testing db's and other slow resources, refactoring for increased testability, testing private methods and a bunch of the other issues that come up on the TDD lists, but I expect it's going to keep me busy for the next month or two really *getting* this. But when I find myself dumb about something, I always figure I'm as well to take the hit now. Getting smarter about testing isn't going to get any easier as time goes by!
# Posted By Peter Bell | 4/16/08 12:02 PM
I'd also add these two things, piggybacking off of Sam (again, not directed so much at Peter but at other readers):

1) Don't shoot for 100% test coverage. It's too damn hard to get there, and probably isn't worth it. Every "good" book I've read (or skimmed) so far on recommends 85%. which may be arbitrary, but I think the point is, 85 isn't 100.

2) Be very wary of downloading a unit test framework and generating tests for all your existing components. There's something really demoralizing when you do that, run them the first time, and see 500 failed tests.

Take baby steps. Get good momentum. Build good will between yourself and your tests. Don't shoot for the "be-all, end-all, I've tested every single corner of this entire component" test at first, i.e. swing for doubles, not homers. When i started writing tests, I did this, and it bit me in the butt a lot, because i'd end up writing really brittle tests that would pass one day but fail the next because they relied on external data or some such thing.

I'd also suggest this as a good start for building good will between yourself and your tests: try it out on an existing bug in a function that isn't 5000 lines long. you got a bug, write a testcase for it. try to write a single test function that proves the bug. then set to fixing it. even if the component under test has 20 other functions in it, try to fix just the one function that's busted. It's such a great feeling when you prove a bug is fixed by seeing the green bar!
# Posted By marc esher | 4/16/08 12:03 PM
@Marc, Great advice! I'd also suggest that if you do have a small green field project (especially libraries or other components you plan on reusing) it feels really good when you create a nice set of tests for them as it really provides that feeling of confidence. The other benefit with greenfield is you can get some of the benefits of TDD rather than just the benefits of unit testing.

Oh, and great preso at Boston from what I hear!
# Posted By Peter Bell | 4/16/08 12:14 PM
Thanks Pete. I totally agree on the green field aspect. My first CF unit test was written on a super tiny, single-component application. I had been writing junit tests for a while, but never for CF. We had this little problem to solve that we might have needed to present to IT in another division. and I knew they were all java dudes. So I wanted to be sure that if I sent them code to look at that it was all enterprisey, because I didn't want them busting my balls since we're a CF shop and you know how some (all?) java people look at CF. It was nice having that motivation.
# Posted By marc esher | 4/16/08 12:19 PM
I strongly recommend check out "Working Effectively with Legacy Code." I know it might seem odd to bring it up in a TDD post but Marc's comment reminded me of the book It really has a wealth of information that is applicable to new code to keep it from feeling like legacy code.

http://www.amazon.com/Working-Effectively-Legacy-R...
# Posted By Adam Haskell | 4/16/08 10:26 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.