CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

Does Effective TDD Require OOP?

I’ve had several conversations lately around the idea that Test Driven Development either requires or enforces good Object Oriented Programming.  I’ve always been a bit unclear in my own mind on whether doing TDD leads me to writing better OOP, or whether I try to follow good OOP practices just to take advantage of TDD with a minimum of friction.  What I can tell you from painful experience is that TDD will expose badly factored OO code.  You might say that doing TDD is like an audit on your OO design.  Anytime doing TDD on your code becomes difficult you need to reexamine your class structure. 

 

One of my coworkers likes to say that TDD just leads you to a different solution.  I’d argue instead that TDD might just be putting a microscope onto your code to find flaws, and we all dress better and stand up straighter when more eyes are upon us.  Besides, I’d say that the code qualities that lead to testability are loose coupling, high cohesion, and a thorough separation of concerns.  In other words, just plain, good, clean Object-Oriented code like we should have been writing anyway.  One of the nice facts about TDD is that testability quite happily coincides with being good code.

 

I’ve heard a couple people say that you’ll understand OOP much better when you start trying to do TDD.  I can certainly echo this experience.  I thought I was an OO guru because I could spout off GoF patterns at will.  On my first project with TDD I was abused of this notion in a painful manner.  I understood basic layering, but I didn’t know how to truly isolate business and user interface functionality away from the database.  I let an overly complex security scheme get in the way of even the simplest business logic testing*.  I guess you’d say my first lesson in Inversion of Control/Dependency Injection was learning to push a custom permission set into code during unit tests without having to wire up data in a database or configuration files.  Once I made the refactoring to make the permission set decoupled from external dependencies, unit testing started to get easier.  The light definitely came on in my head after that.

 

Writing top down procedural code will not lead to testable code.  You simply cannot create code that can be effectively unit tested in the TDD style with procedural code.  You can’t isolate functionality to the same degree.  Loose coupling is nearly impossible.  What’s even more difficult is trying to retrofit unit tests to existing procedural code.  A terrible, but common, trap to fall into as a TDD newbie is to forgo writing the tests first when you don’t know how to write the test.  Just starting to code thinking you’ll write the automated tests later after you make the code work is a slippery slide into technical debt hell.  The resulting code isn’t written with testability in mind and the newbie will often find retrofitting the new code with unit tests laborious or even impossible.   The usual outcome is either the tests are too coarse-grained to be easily understood and debugged, or the test coverage is lacking.

 

You simply must make testability be in the forefront of your mind anytime you’re writing code.  For TDD, it’s a better approach to decompose a coding task into the distinct responsibilities and code the constituent classes in order to build the whole from the bottom up.  Pick off the first responsibility, code a class or method to do only one responsibility, rinse, and repeat.  Only when you have the individual pieces working and unit tested do you glue them together and test the whole.  Often times I find that it’s easier to “see” the whole picture after a few of the pieces are built.  Little self-contained pieces of code with minimal coupling on other pieces of code will be easier to test.

 

I think a lot of people struggle with TDD because they’re used to writing procedural code.  Learning to effectively decompose code into classes with well defined responsibilities is essential to doing TDD without pulling your hair out.  I’m not trying to bash procedural coders.  I’m just trying to warn them that they’ll want to work in a more OO fashion to reap all of the benefits of TDD.

 

Your TDD experience will be much more pleasant and successful with stronger OO skills.  You have to be cognizant of coupling and cohesion properties for every single class interaction and method signature to keep the unit tests and code flowing.  A TDD Jedi is fully steeped in OO programming and is mindful of the qualities of his OO decisions.  Writing willy-nilly procedural code without unit tests is a seductive path to the Dark Side.

 

Am I being too harsh about procedural coding?  Is good OO synonymous with effective TDD, or am I just being an OO bigot?  Fire away.

 

EDIT 11/30/2005:  Take a look at Bill Caputo's post at http://www.williamcaputo.com/archives/000245.html for a thoughtful look beyond OO for Agile development.

 

 

* Dave, Levi, Vivek – if you’re out there, here’s a belated “I’m sorry”



Comments

Jeremy D. Miller said:

via Don "OO is dead, SOA is everything" Box, check out a differing opinion from mine at http://www.xsltblog.com/archives/2005/11/is_the_agile_de.html
# November 30, 2005 11:26 AM

Jeremy D. Miller said:

I forgot to say in the post that I've never gotten to do any serious work in a Functional language. I have no idea what the impact on TDD is for these languages. My brief usage of a Prolog analogue was a very negative experience, so I'd still say you have to have TDD.
# November 30, 2005 11:30 AM

Fregas said:

Maybe I need to look at TDD some more. I don't see many developers really doing OO or really doing 3-layer design. Like you mentioned, data access is mixed with business logic, business logic is in the page/code behind and they try to do a lot in a procedural manner. I'm not saying I'm not guilty of this too-- I am. But I've gotten a lot better in the past 2 years. But I seem to be alone or amost alone at my company. My boss (the lead developer) doesn't care about OO at all. Its just not a priority with him.
# November 30, 2005 12:10 PM

Jiho Han said:

Can you go more into your experience involving that custom permission set? I would love to hear about it in detail...
# November 30, 2005 1:43 PM

Hans Nowak said:

Do you have any more-or-less concrete examples of procedural code that cannot be tested as well as OO code? I am curious to know what the problems are.
# November 30, 2005 7:25 PM

Maruis Marais said:

I completely agree with you. A few years ago when .NET arrived I was forced to switch from VB6 to C# and was surrounded by Java developers. My OO skills went from virtually nothing to pretty decent. In the beginning I was very frustrated with all the small methods they used to code into their assemblies. It always felt like every third method did a small amount of work and the rest just delegated.

Well, today I can say that I understand why and now I'm the one harping on about TDD and having well factored code. Using Design patterns and making sure you have low cyclomatic complexity, etc..etc..
# November 30, 2005 11:17 PM

Chris said:

Yes, TDD does require OOP. Because refactoring and abstraction are core concepts in TDD. Done much refactoring with purely procedural code? It's like trying to reshape steel. Sure, with a lot of blood, sweat and tears you might be able to do it, but who would want to?

OOP provides the tools necessary to do TDD: abstraction, polymorphism, inheritence, encapsulation, overloading. Without the tools, I can't see TDD ever being more than a twinkle in someone's eye.
# December 7, 2005 6:36 PM

Jeremy D. Miller -- The Shade Tree Developer said:

I promised myself that I wouldn't ever make another post about stored procedures, but Eric's post on...
# May 25, 2006 3:47 PM

Jeremy D. Miller -- The Shade Tree Developer said:

There's an interesting thread going on in the Yahoo TDD group started by Jay Flowers about introducing...
# June 27, 2006 6:10 PM

Mike said:

The obvious response is "no, OOP makes TDD harder".  But then I saw that you assumed all the world is either "procedural" or "OO".  What a strange dichotomy...

TDD is far, far easier with functional programming.  A function has inputs, and outputs.  A test provides inputs, and checks outputs.  It's a perfect match.

You don't need a functional language to do FP (though it certainly helps).  Just stop having functions do side-effects!  TDD seems to push you in this direction: a function with side-effects is necessarily harder to test.  That is, TDD is a way to help push your OOP toward FP.

Don't let a "brief usage of a Prolog analogue" spoil FP for you.  If you're doing TDD, you're probably closer than you think.  Maybe "OOP with TDD" is the new buzzword-savvy enterprisey way to say "FP".

# May 18, 2007 5:11 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP