Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Succeed with TDD by designing with TDD

After 2 years of using Test Driven Development on .NET projects I'm a believer. Effective TDD leads to cleaner code, fewer bugs, and superior architectural qualities. Systems written with TDD are easier and safer to modify and deploy, leading to a reduced cost of ownership over the lifetime of an application. Yes, TDD means a lot more code to write. Yes, TDD can make the activity of writing code take longer, but it can and does shorten the time to delivery of working, production-quality code.

Using TDD dramatically changed the way I approach OO coding for the better, but I made a lot of mistakes along the way. The easiest way to learn TDD is to work closely with teammates that are already comfortable with TDD. Otherwise, you are on your own, and a lot of the writing I have seen on TDD falls into one of two categories -- "TDD is Cool" and "How to use NUnit." You've got to start somewhere, but when faced with the very real world of enterprise architecture (databases, messaging, user interfaces, Active Directory), the simple examples of create an object, pump in some inputs, and validate the results suddenly don't seem to be enough.

In my experience, newcomers to TDD often struggle with the mechanical work of writing unit tests, negating most of the value of TDD by writing unit tests that are too coarse. A common complaint is that the tests are too hard to write, or just take too much time to write. Frequently, well-meaning developers will "just make it work" first, and retrofit tests around the new code later. I did this pretty often on my first TDD project -- with the result largely being a set of fragile tests that flat out sucked and broke the build anytime someone breathed too hard on the codebase.

The usual culprit is the way that the code is written and structured -- god and blob classes, stovepipe architecture, poor separation of concerns, and an overuse of static methods. So how do we write code to maximize the efficacy of TDD? The most important thing is to write good OO code (I'm not trying to exclude folks doing procedural or functional coding, but this post is meant primarily for .NET developers). A couple years most of my team of System Architects (sic) were sent to an introductory(!) class on OOA/D and came back giggling and blathering about code being "Highly Cohesive" and "Loosely Coupled." All those fuzzy sounding OO qualities we've known about for years aren't just a way to impress other developers while stroking your beard, they are absolutely necessary to easily utilize TDD. It's time to take these concepts seriously in the .NET world. Read this from Scott Bellware on TDD in the Microsoft community versus the Java world. A bit more on cohesion and coupling later.

Which Comes First, Chicken or Egg?

A TDD developer follows a continuous cycle of writing a small, isolated unit test for a little bit of functionality, then writing a little code to make the test pass. Ideally, an individual unit test involves a discrete chunk of a single class (or a few classes). How do you know if your unit tests are small/isolated/discrete enough? Easy, VS.NET debugger sessions become uncommon and short and test failures are easily diagnosed and remedied.

So if small unit tests are good, how do you design your code to maximize the TDD goodness? By designing with TDD in the first place.

Using TDD as a Heuristic

From Merriam Webster - Heuristic: "involving or serving as an aid to learning, discovery, or problem-solving by experimental and especially trial-and-error methods " Simply put, TDD is a technique for determining class structure by making testability a first class consideration in your design. Focusing on testing a unit of code at a time leads to creating cohesive classes with a distinct purpose and responsibility. The need and desire to quickly setup an isolated unit test on a class will lead to a loosely coupled design. Here's a couple of quick questions answer to test whether your class structure really exhibits desirable architectural qualities:

  1. Paraphrasing Michael Feathers, can someone fire up VS.NET on your code, take any arbitrary method, and write a unit test in a short amount of time?
  2. Can you completely remove your computer from the network and run 100% of your unit tests covering both user interface and business logic code? Using a local database instance or a wireless connection does not count.
  3. Can you write tests that only involve one or a very few classes?
  4. If you are writing a web application (or a Web Service for that matter), can you run unit tests on much of your user interface code with IIS turned off?

Test Driven Development is an Alternative Design Process

There is far more to TDD than automated unit testing. Much like UML modeling or CRC cards, Test Driven Development is a process to explore a design and arrive at a good solution. The difference, in my mind, is that TDD is a "bottom up" process, where other design techniques are "top down." The truly good practitioners focus on rapidly building discrete, working pieces of code, then arranging the coded classes into an emerging structure guided by a knowledge of good design principles and a strong working understanding of Design Patterns.

Some of the impetus for using UML or CRC is the belief that it easier to explore design ideas by using abstract non-code artifacts because code itself is too difficult or inefficient to change once it is written. TDD can turn this assumption on its head by making code easier to change and restructure because the resulting code is cohesive, and safer to change because a good automated unit test suite can catch errors resulting from the changes. A good refactoring and code navigation tool like ReSharper (JetBrains rocks!) makes this type of malleable code assembly more efficient.

One way to think about TDD is an analogy to Lego blocks. The Lego sets I had as a child were the very basic block shapes. Using a lot of little Lego pieces, you can build almost anything your imagination can create. If you buy a fancy Lego set that has a single large piece shaped like a pirate's ship, all you can make is a variation of a pirate ship. Over the next month or so, I'm going to blog a TDD Starter Kit of design concepts and strategies. Keep watching this space, the next posts will be shorter and actually contain code.


Posted Wed, Jul 20 2005 4:54 PM by Jeremy D. Miller

[Advertisement]

Comments

Jeffrey Palermo wrote re: Succeed with TDD by designing with TDD
on Thu, Jul 21 2005 6:09 AM
Great post! TDD is very straight-forward with class libraries, but I'm having a hard time right now because I'm writing web controls. I need to be able to test my web control in an HttpContext without running an entire web page.
Jeremy D. Miller wrote re: Succeed with TDD by designing with TDD
on Thu, Jul 21 2005 6:51 AM
Jeffrey, it might be an unpleasant experience, but I *know* that it is possible to create an HttpContext and make it current in a unit test. I've seen it done, I just didn't go look much at how they did it. You'll have to fire up Reflector to pull it off I'd imagine.

Let's talk about it at the Agile lunch today. I'm coming up on my first ASP.NET development in 2 years pretty soon.
Rob Caron's Blog - A Team System Nexus wrote Suggested Reading - 2005-07-31
on Mon, Aug 1 2005 2:58 AM
Cleaning-out my “To Blog” file again…
Architects

Handling data in service oriented systemsEdward...
TSHAK wrote TDD and Legos
on Mon, Aug 1 2005 5:12 PM
Jeremy Miller recently made a great post on designing with TDD. The following is a great analogy on TDD's...
Jeremy D. Miller -- The Shade Tree Developer wrote Haacked on TDD and Jeremy's First Rule of TDD
on Fri, Oct 21 2005 12:02 AM
Unit Testing Loves Beta Testing And Vice Versa
Phil Haack has a great post up in response to some folks...
Jeremy D. Miller -- The Shade Tree Developer wrote Microsoft’s recommendations for Test Driven Development are wrong!
on Thu, Nov 17 2005 11:35 PM
Okay, wrong is too harsh, but I made you look.  Microsoft has published a list of recommendations...
Jeremy D. Miller -- The Shade Tree Developer wrote Microsoft’s recommendations for Test Driven Development are wrong!
on Fri, Nov 18 2005 8:53 AM
Author: <a href="http://codebetter.com/blogs/jeremy.miller/">Jeremy Miller</a>
<br>
It *is* a good thing that MS is even talking about best practices like unit testing and build automation. That being said though, Microsoft seems to be trying to redefine TDD to match up with their tools instead of creating tools that support TDD best practices.
Jeremy D. Miller -- The Shade Tree Developer wrote Achieve Better Results by following Jeremy's Third Law of TDD: Test Small Before Testing Big
on Tue, May 30 2006 2:43 PM
Getting back on track with TDD content.  One of the most important lessons learned the software...
Jeremy D. Miller -- The Shade Tree Developer wrote Best of the Shade Tree Developer (with actual links!)
on Mon, Aug 7 2006 4:51 PM
Between being extremely short handed at work, tech' reviewing a new book, a
possible book proposal...
Jeremy D. Miller -- The Shade Tree Developer wrote Best of the Shade Tree Developer (with actual links!)
on Fri, Sep 1 2006 2:33 PM

Between being extremely short handed at work, tech' reviewing a new book, a possible book proposal

papo we(b)log wrote Mock parziali: una alternativa ai delegates.html
on Wed, Nov 29 2006 8:12 AM
Jeremy D. Miller -- The Shade Tree Developer wrote Jay's TDD QuickStart, and the underlying problems he stumbled into
on Wed, Mar 7 2007 2:05 PM

Jay Kimble , CodeBetter's resident AJAX guru, issued a little challenge to us TDD bloggers about using

Dim Blog As New ThoughtStream(me) wrote Excellent List of TDD Starter Blog Posts
on Wed, Mar 7 2007 5:05 PM

Jeremy Miller has posted quite a response to a question asked by Jay Kimble on unit testing with the...

Chad Myers' Blog wrote Starting a new set of projects, what to do?
on Mon, Oct 29 2007 3:58 PM

Starting a new set of projects, what to do?

Troy DeMonbreun wrote re: Succeed with TDD by designing with TDD
on Fri, Feb 22 2008 4:20 PM

I would consider TDD as a heavily Top-Down process, given that your tests depend on some sort of mocking/stubbing of dependencies that often don't exist yet, and in the sense the TDD community seems to heavily support DI as part of TDD (i.e.: DIP is top-down -- en.wikipedia.org/.../Dependency_inversion_principle).  Also, most TDD practioners, when explaining TDD, give Top-Down examples.

Could you elaborate on how you feel TDD is Bottom-Up?

Jeremy D. Miller wrote re: Succeed with TDD by designing with TDD
on Fri, Feb 22 2008 4:59 PM

@Troy,

First, this post is ancient.  Top-Down vs Bottom-Up might not be a useful argument at all in regards to TDD because I'll do either depending upon the situation.  With TDD you start with whatever task you *do* know how to do and build and test that first.  I often design the interface in the middle of writing a unit test for the client that's going to consume that interface.  It's not necessarily prefactored other than starting with the idea that certain responsibilities are fulfilled by different classes.  Like I'll write a Presenter class that interacts with a View and maybe a Repository.  I might develop the Presenter before the other pieces and generate the View and Repository interfaces in the course of writing the Presenter.  You can call that top down if you want.

Other times, I might know how to do one granular operation of a much larger algorithm, but not the whole.  I'll build some of the individual steps using TDD in isolated pieces, and let the whole of the algorithm code emerge from the structure that is suggested by the little pieces.  That I would call bottom up.

More here:  codebetter.com/.../145752.aspx

Jeremy D. Miller -- The Shade Tree Developer wrote A Train of Thought – August 24th, 2008 Edition
on Sun, Aug 24 2008 5:05 PM

Thank God I don’t have my old 3 hours a day of commuting into Manhattan, but I needed to spit out some

A Train of Thought ??? August 24th, 2008 Edition - taccato! trend tracker, cool hunting, new business ideas wrote A Train of Thought ??? August 24th, 2008 Edition - taccato! trend tracker, cool hunting, new business ideas
on Sun, Aug 24 2008 5:25 PM

Pingback from  A Train of Thought ??? August 24th, 2008 Edition - taccato! trend tracker, cool hunting, new business ideas

Community Blogs wrote A Train of Thought – August 24th, 2008 Edition
on Sun, Aug 24 2008 6:25 PM

Thank God I don’t have my old 3 hours a day of commuting into Manhattan, but I needed to spit out some

Mirrored Blogs wrote A Train of Thought – August 24th, 2008 Edition
on Fri, Sep 5 2008 12:42 AM

Thank God I don’t have my old 3 hours a day of commuting into Manhattan, but I needed to spit out some

Jeremy D. Miller -- The Shade Tree Developer wrote Design and Testability
on Wed, Dec 10 2008 12:25 AM

My most recent Patterns in Practice article entitled Design for Testability is available in this month's

Community Blogs wrote Design and Testability
on Wed, Dec 10 2008 12:37 AM

My most recent Patterns in Practice article entitled Design for Testability is available in this month's

Using TDD as a heuristics « hotrannam’s corner wrote Using TDD as a heuristics « hotrannam’s corner
on Thu, Jan 22 2009 12:57 PM

Pingback from  Using TDD as a heuristics « hotrannam’s corner

Design principles and TDD « hotrannam’s corner wrote Design principles and TDD « hotrannam’s corner
on Mon, Jan 26 2009 4:10 AM

Pingback from  Design principles and TDD « hotrannam’s corner

Jon Kruger’s Blog » Blog Archive » TDD Starter Kit - Sample Projects and Links wrote Jon Kruger’s Blog » Blog Archive » TDD Starter Kit - Sample Projects and Links
on Thu, Jul 23 2009 11:47 AM

Pingback from  Jon Kruger’s Blog  » Blog Archive   » TDD Starter Kit - Sample Projects and Links

Add a Comment

(required)  
(optional)
(required)  
Remember Me?