Welcome to

Magenic Technologies Community Blog

Sign in | Join | Help

Aaron's Technology Musings

Who let this guy on the podium?

Confession of a former (and to some degree, continuing) TDD skeptic

Boy did I have to be dragged kicking and screaming to the kool-aid.

I have a confession to make.  Last Friday, I wrote my first “official” unit test.  I say official, because in my career, I have written lots of what I used to call “unit test throwaway UIs” to exercise parts of a codebase that had some level of complexity.  But why the change?  Why the sudden embrace?

To understand “why now”, lets me first go to my reasons for skepticism.  First of all, I hate the term TDD.  Maybe I have seen too many cases in my career of the technology tail wagging the dog to believe that an artifact of development, no matter how useful, should “drive” development.  I believe in “Profit Driven Development”... everything else is just a means to an end.  That, of course, started me on the wrong foot with this “new thing” (well, not so new, agile and TDD have been around for awhile).  My first step in overcoming my over-skepticism with TDD, and by guilty association, unit testing as primary development driver, was to get past my aversion to the term and look at TDD strictly as a tool in the toolbox.

The next step was making the mental jump to fully embrace OO.  I will freely admit that coming to Magenic and seeing OO done right helped me here.  We have to admit... there is lots of really bad OO code out there.  And having worked in consulting for a very long time, I have seen LOTS of it.  In fact, for years, I would come in to an engagement, often later in the project, and become a hero by bypassing some elaborate OO structure that, while well modeled, was completley impractical, and writing a nice database stored procedure that accomplished the same task in 1/1000th of the CPU cycles.  I did this so many times, with such success, that “removing OO cruft” became one of my personal favorite drunken rants at the company holiday party!

By contrast, when I joined Magenic, one of the first things I received in the mail was Rocky's CLSA.net book.  Which demonstrated a pretty darn good platform for doing good OO work.  I also came to a client where I immediatley joined other developers who were doing good OO work - where the designs accomodated both good modeling and good performance.  It helped that the .NET 2.0 framework supports generics, which removed a lot of the barrier to writing good collection classes, but that was more like the icing on the cake.  It was the first time since about 1997 that I truly believed that OO was practical for run of the mill, “forms over data” projects.

Why does OO matter here?  Well, a typical non-OO app in .NET -- think your garden variety ASP.NET site quickly developed in the VS03 IDE -- will link together some server side controls, typed datasets, and if you are lucky, stored procs against a SQL server database.  There might be a few routines that massage the typed data sets or do some validation, but for the most part, the middle tier server would be stateless.  Of course, this has a lot of tradition in Microsoft based development - I remember going to, of all things, Developmentor's Guerilla VB class around January 98 (might have been the inagural one, if my recollection is correct) - and being drilled on how “state = bad” on the middle tier.  Now, of course, much has changed since back then, but if you have no state on the middle tier, OO becomes something you might be less inclined to do (i.e. if all your objects are transitory within the context of a web request, that might be more overhead than you need). 

The point here - is that most of the application code - without OO, is simply wiring between typed data sets and bound stuff on the UI.  How do you unit test that?  While it is possible to unit test stored procedures, or for that matter, the UI, the arguments sound like the kind of arguments I made about the OO-ness of VB5 - yeah, you could do it, but you were really twisting the paradigm to do so.  Besides, it really can be hard to convince a team to code up unit tests to prove that “select * on foo” works.  And skepticism remains on UI unit testing - heck, I still remain unconvinced that UI unit testing is a useful exercise.  Without OO, a person starting down the path of unit testing, and eventually, full on TDD, will see a lot of work with very little perceived benefit.  Most wont bother.  Most unit tests inside that kind of scenario cross layers, something we are told actually makes a unit test into something else - an integration test.

In other words, when you have a pervasive coupling between your layers, unit testing any particular thing becomes either impossible or useless.

However, once you have OO, you can really start to decouple your layers.  Objects can be instantiated that function in their own right, without a database call required to easily populate them.  Once objects are more than simply “places where the data is” (aka glorified structs) - there is actual behavior to test.  Once you are testing behavior, unit testing becomes much more interesting - and useful.  Unit testing truly becomes a means to “knowing this thing works” - just like I know “select * from foo” is going to give me all the records in the table foo, I know that this code, now that it has test coverage, is going to do what it is designed to do, within reason.  Unit testing becomes a way for me to decrease the number of places I have to look at when debugging... something that saves me a ton of time later in the project, when I am in burndown mode addressing bugs.

So - do I go for 100% coverage.  No.  I have not drank that much kool aid yet.  While it is tempting, I am not going to waste my client's time writing a unit test for simple accessors that return an internal variable.  Maybe I will if the tests are codegenned for me (seems to be very doable - heck, maybe I just need to find that option in the VS.NET 05 toolset).  I will cover every non-trivial operation capable of producing logic bugs.  For example, I recently wrote a property setter that takes freeform input (i.e. 4 x 12 EA) - and parses the two numbers, and the unit, out, into seperate fields.  That is the kind of operation that screams unit test.  I also wrote tests for a search algorithm, and every method on my main classes. In fact, I wrote some of the tests before the methods had an implementation, according to the requirements of the project.  I didn't even know I was doing TDD - and yet here I am, writing my tests first, then coding to pass the test.  And it really wasn't that bad.

That said, some moderation is in order.  Doing good TDD seems to have some preconditions.  One is that you are doing good OOP.  Another is that you have a good idea of what the requirements, in fact, are (more often than not, this is actually a problem).  Third is that you are working with others who either are “on the boat”, or are willing to get on board with the idea.  The fourth, and perhaps most controversial from my standpoint, is that you should use TDD as a way to make your  life easier and your code better.  If you start talking to CxOs about doing TDD, they won't care 99% of the time.  TDD has to translate to something meaningful to the sponsor - and should be communicated as such whenever the software methods are discussed outside the development group.  TDD has to be driven by PDD (profit driven development) - in order to actually gain traction.

Unit testing, and eventually TDD, is a great tactic.  As with all good things, in moderation, it can do great things for your project.  So long as we don't claim start claiming it will save the world and bring forth world peace, people should continue to take it seriously.

Published Thursday, November 02, 2006 2:30 PM by aarone

Comments

# Dont Write Poo @ Tuesday, March 06, 2007 9:32 AM

http://www.codinghorror.com/blog/archives/000801.html Jeff Atwood at CodingHorror strikes again... speaking

Aaron's Stupid Technology Musings

New Comments to this post are disabled
Powered by Community Server, by Telligent Systems