Why you fail with TDD
I’ve been working hard the last six months teaching my teammates about unit testing, code reviews, SOLID, SCRUM
and anything else I think we can benefit from. I feel lucky – for the
most part they are open minded and accept my ideas and try them without
prejudice, working this way has enabled better code and work for both
the team and myself.
Over the last months the team’s productivity has increased, the unit
tests got a lot better and the code has improved dramatically. But not
all is peachy - There is one methodology that I’m having only partial
success in convincing my team that they should use – namely Test Driven Development (TDD).
While some of the team use TDD exclusively others still find it too hard
and/or cumbersome to use on their daily tasks. I know they’ve tried,
and failed using TDD for some of their tasks and now the only reason
they keep on trying is because I insist they do it – without real
benefits.
Why some fail while others succeed
I wanted to investigate why TDD worked great for some while completely missing the point for others – could it be that the tasks of the developers that used TDD were easier to test? perhaps the developers that didn’t manage to apply TDD worked more with complicated legacy code that was just harder to test?After much investigation I came to the conclusion that to some extent the success or failure of TDD depends on how easy it is to write tests for that piece of code which is effected heavily from the amount of code existing before the change and how well it was previously tested. But that’s not the whole picture, when I look at the code written by the developers that found TDD to be “just too much work” I almost always see more than one assert which indicates that they might be trying to test too much –and if you write the tests before the code means that it’s a lot harder to do that when you’re actually planning on implementing a few features at once – not very TDD at all…
The TDD way
TDD is not about the tests – it’s about design:
By writing the test you actually create a form of specification (singular not plural!) and by making the test pass you satisfy the requirement. By this logic multiple asserts means (more often than not) that you’re implementing multiple requirements at the same time. Another issue with trying to write a test for multiple requirements is that the test tends to be much more complicated than a test that checks only one thing.
One final thought
Multiple asserts is not necessarily a bad things, there might be a valid reason to writing multiple asserts to test a single requirement.I have a trick to check if a test maps to more than one requirement - ask the developer “what are you testing here”, if he uses “and” in the sentence i.e. “I make sure that the username is valid and an email is send” he might be testing too many things.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Amin Abbaspour replied on Mon, 2011/01/17 - 8:05am
"TDD is not about the tests – it’s about design"
dude! this was excellent.
Dieter Luypaert replied on Mon, 2011/01/17 - 8:13am
Endre Varga replied on Mon, 2011/01/17 - 9:03am
Sasha Arsic replied on Mon, 2011/01/17 - 9:08am
Dean Schulze replied on Mon, 2011/01/17 - 10:38am
Endre,
Your comment reminds me of this attempt by Ron Jeffries to TDD his way to a Sudoku solver, compared to Peter Norvig's solution:
http://xprogramming.com/xpmag/OkSudoku
http://ravimohan.blogspot.com/2007/04/learning-from-sudoku-solvers.htm
(Tags aren't working in this thing, again.)
I think the real lesson from this fiasco is that Agile zealots won't admit when their methods don't work.
Dean Schulze replied on Mon, 2011/01/17 - 10:40am
I've heard TDD referred to as "Big Test Up Front", and the diagram reminds me of the waterfall diagram. Has agile taken us from BDUF to BTUF?
Nicolas Bousquet replied on Mon, 2011/01/17 - 10:56am
The thing is TDD alone give you nothing.
The fact is for TDD, you have to already perfectly know the solution and work in an environment where there is good code coverage.
When dealing with any existing code (even code written last week by your teammate), problem is that you do not master it. So basically you don't really know what it really do, what to expect, and what to do to add this new small functionnality or how to correct this bug.
You know challenge is not to make a unit test that check you correctly load the new parameter from the configuration file or the database. The problem is not to write this simple if depending of whether the parameter evaluate to true of false. It is really trivial.
The problem is figuring where to put this test in thoses 13 millions lines of code (my case at work). What are the real inputs, and what output are expected.
You can "try" and of course you try. But this is not Unit Test that will give tell you what is the good output. It is not even the specification. What give you the solution is running the damn software, debug, inspect the variables, inputs and output. Insert thoses 2 or 3 lignes of code. Run again, verify. Check that you was wrong... try again...
Basically what you use is a debugger together with the real application with production data (not to say it run on production system !!!).
When you have finally corrected the bug, if the code you modification is based on is already well tested, designed for testing, you can add a new unit test losing only a few minute for it.If it is not designed for testing, just testing the trivial addition you made could require a day of coding and you will not do it because you want to correct a bug per hour, not a bug per day.
This is not to say TDD is bad. This is to say TDD is not the silver bullet some try to convince you it is.
Josh Marotti replied on Mon, 2011/01/17 - 2:20pm
in response to:
Dieter Luypaert
Dieter, look at the graph again. If the tests succeed BEFORE you write code, then you have bad tests ;)
Step one assumes no code is written. You write tests first, then write code to pass the test.
Sivaprasadreddy... replied on Mon, 2011/01/17 - 8:41pm
Hi,
Nice post. IMHO TDD will succeed if the team is technically good, committed to write good code, otherwise project will end up with unmaintainable code and useless outdated unit tests.
Why i explicitely said "committed to write good code" is i have seen lot of developers who just think we can write in any way if it works for now, they don't worry if its not a good practice, or it is a bad design.
Martin Groenhof replied on Tue, 2011/01/18 - 8:45am
TDD, is like putting the Cart in front of the Horse, it does not work because it breaks the whole mentality of development, make something work -> see if it solves the problem -> repeat if necessary -> solution -> test it
TDD does not offer you the flexibility of trying out 100 different solutions, you HAVE to get it right the 1st time, who in their right mind would write 100 different test cases 1st and than try wrting code to fill them, bonkers
Test yes, but only when you're satisfied with what you got and think it works
TDD was invented because the Evangelists needed a new product to sell
Cedric Beust replied on Tue, 2011/01/18 - 1:29pm
Andy Leung replied on Tue, 2011/01/18 - 6:41pm
BTW, the TDD you have in the drawing is more of a Unit Testing to me than TDD. So if developers are doing Unit Testing on all "units" they build, isn't that best practice that is applicable to waterfall, Agile or any other approach? So really there is no TDD such thing?!
Stephanie Gacho replied on Tue, 2012/05/29 - 9:18am
Kookee Gacho replied on Mon, 2012/06/11 - 10:36pm
Carla Brian replied on Sat, 2012/06/30 - 11:03am