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”

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. 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 is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Test Driven Development. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.ironmaiden.com/ Powerslave

    This wasn’t too close to what I was googling for, but I’ve found it a lovely article, with which I completely agree.
    Thanks for your interesting thoughts!

  • http://www.backlinkschecker.ws/backlinks/buylinks.html Purchase High PR Backlink

    I fully support this panel, thank you.

  • Mike

    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”.

  • http://www.chrisholmesonline.com Chris

    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.

  • http://exceptionz.blogspot.com/ Maruis Marais

    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..

  • http://zephyrfalcon.org/ Hans Nowak

    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.

  • Jiho Han

    Can you go more into your experience involving that custom permission set? I would love to hear about it in detail…

  • http://www.dotnettricks.com/ Fregas

    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.

  • Jeremy D. Miller

    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.

  • Jeremy D. Miller

    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

  • Gérard Mendes

    I totally agree with you.

    I seem to have a hard time to convince procedural coders to try TDD, mainly because it doesn’t allow them to work the way they always work.

    The experience they have with it is made of two things
    – frustration because of the small steps taken to implement each test
    – frustration because of the time spent thinking about the interface of the objects
    – frustration because of the use of advanced testing techniques that rely on oo-concepts (mocking relying on ocp, etc.)
    – frustration because of the unavoidable and seemingly useless session of refactoring after each test (which alter the not-so-perfect design they thought of in the first place)

    As a skilled OO programmer, I must admit I never had to work around these barriers, and I understand how hard it is to get into TDD without OO skills. It is feasible, but it requires help and mentoring (pair programming anyone ?)