Introduction
Bertrand Russell was concerned about the issue of having to accept certain axioms to be able to proceed with an education i...
For further actions, you may consider blocking this person and/or reporting abuse
One more thing... here’s a gem that is worth sharing, which I believe is attributable to Chris Raser.
The Cycle of Misery
by Chris Raser
The code for the new feature is “too simple to test.”
The code grows.
Each change is “too simple to test.”
The changes start to interact.
And produce strange edge cases.
Some of these edge cases are intentional, some are accidental.
Other code is built that depends on the current, undocumented behavior.
The actual requirements are lost to time.
The code is now “not really testable.”
The code has become thousands of lines of if-else chains.
Tons of copy-and-pasted code.
Rife with side-effect booleans.
It has half a billion possible states.
And cannot be understood by mere mortals.
Nor refactored without risk to the business.
A new feature is needed.
A bright, shiny new bit of code is created.
Because adding anything more to the old code is madness.
The code for the new feature is “too simple to test.”
Exactly this. The "Gilded Rose" kata is a great illustration of this principle in action. I've had the pleasure, on no small number of occasions, of diving into those files with many thousands of lines, some functions in the many hundreds of lines, and trying to sort them out so that you can confidently add new functionality. As Sandi Metz illustrates on her excellent talk on the Gilded Rose, sometimes the best (only?) way out of those messes is to rewrite from scratch. If you don't have good, intentional, design-oriented tests to tell you what the intent of each part of the code is, you may well be looking at several months of effort to get back to full functionality. The only way to prevent it is writing good tests at every step. Whether TDD or ITL, get those tests in early while you know what it is you've written and why! >.<
The problem of TDD is that it requires a lot of mental strength and a solid development process to keep alive.
Developers will throw it out of the board as soon as their managers start breathing at their necks.
On top of this, it takes one dev getting away with poor testing for the whole team to cheat around the unit testing part as well, making it useless (actually, worse than useless, because they need to be maintained).
Edit - disclaimer: I do like TDD, although I think that the usefulness of it is not in having those unit tests in the first place but in forcing you to design your code better so it is testable, understandable, and with less redundant / complex parts.
That is all true. I find working in that scenario really taxing.
I have found developing a culture of automated testing can be really difficult but also really beneficial to a team though. I have seen teams that don't write tests at all, exhibiting much of the symptoms you mentioned, then transform into a team that really love testing.
I absolutely agree on the point of TDD leading to better design. I found when a team embraces TDD or at least good automated testing, pull request become a much more rewarding process and things like mob and pair programming become a lot more pleasurable and less exhausting. The conversation becomes about the best design and other real concerns.
It's a huge challenge to transform a team that does not embrace testing into one that does but if it can be done, it seems to be worth it.
In my experience — ergo anecdotal for everyone else — TDD works very well when you have the right language and tools to support it.
The tools I used were C#, Visual Studio, NCrunch, and NUnit. Not only did TDD work, it was also fun. Yes, fun. Not kidding. If you work in C# and use Visual Studio, try NCrunch and use either NUnit or xUnit.net and see for yourself.
If you have a language that supports contracts, TDD is superfluous. TDD is for languages that do not provide the ability to specify preconditions, postconditions and invariants. Contracts are something that has to be in the core language, not as a library bolt-on afterthought. Alas, there are not very many languages that provide that facility: Eiffel, D, Spec#, Midori M#. Some others I've heard support contracts, but I haven't played with them myself, such as Ada 2012, Clojure, Perl 6, Oxygene (Object Pascal dialect from RemObjects).
Full disclosure: I'm a D fanboy. Also a Midori M# fanboy, but alas that endeavor got crushed under the wheels of progress.
Some languages, like C++, are not amenable to TDD. The "one minute cycle" of make test, run test: fails, write code, run test: success, refactor, run test: success, check-in is nigh impossible when there is a lengthy build step for each test run. I hope C++20 will have contracts; fingers crossed. (Thanks to Gabriel Dos Reis, Garcia, Lakos, Meredith, Myers, and Stroustrup for working hard to make it happen!)
The other danger with TDD is putting in non-unit-tests in the mix with the TDD-style unit tests. When the unit tests get polluted with performance tests, integration tests, system tests, acceptance tests, security tests, functional tests, end-to-end tests, smoke tests, or behavior tests... then that's bad. Unit tests ought to be written by developers, and run in a debug build, and should take no more than a few seconds to run. The other kinds of tests ought to be written by the quality engineers (except for BDD tests in, say, Gherkin, which should be written by the PO perhaps assisted by BAs, within discussion by Devs & QE), should be run against an optimized release build, and may take hours or even days to run.
On my one project, the entire set of unit tests took seconds to run, and had about 70% code coverage. The integration tests took about 600 hours to run the full suite, if run in serial.
A couple videos on the topic:
I'm a fan of both James Coplien (author of Advanced C++; he's the second C++ programmer after Stroustrup), and Robert "Uncle Bob" Martin. I had a squeee of delight watching these two debate the merits of TDD:
Jim Coplien and Bob Martin Debate TDD
Another person I respect is Rainsberger. He's a strong advocate against using integration tests as a substitute for unit tests. He comes off as a bit abrasive (which I consider passionate), and his putdowns of integrations tests is in the context of using them as a substitution for unit tests. He's not against integration tests used as integration tests where they are suited. The way I think of it, integration tests (written by QE) are at the scope of bricks and mortar; unit tests (written by Devs) are at the scope of electrons and quarks. Completely different domains, for different purposes.
Integrated Tests are a Scam
Great post - it highlights a lot of what I've been grappling with over the past year.
"To me one of the biggest advantages (maybe the most under-appreciated advantage) of TDD is the design that it encourages." -- This has been my biggest takeaway (and thanks for putting it so eloquently). It's not enough to just write a bunch of tests that, once passing, will assure your feature will work. It's taking the opportunity to think about consumer usage and adapting your design to be simple, consistent, and easy to use. I wish I knew how to measure "better design" or its productivity gain or bug density reduction.
TDD has also helped me realize when I write code that isn't quite clear as it becomes hard to test. There may be a valid reason, but it triggers me to consider "are there separable concerns here? Can this be simpler?"
Disclosure: I've adopted something of a hybrid to TDD and ITL. I want awesome regression coverage, but I'm very much used to thinking in terms of solutions. For now, my test code tends to grow with my production code, informing the design and validating I'm addressing edge cases. We'll see if this is just a stopover on my way to TDD.
Thanks again - good read and very fair treatment.
Best analytical article I've personally seen on here, awesome!
For me, having testability and testing early in development, and particularly having a tight feedback loop, is good for my morale and confidence as a developer. Maybe it's the way I was brought up as a developer, but I get really nervous when I write too many lines of code without holding it up to some standard at run-time.
Like others have mentioned, though, adding unit testing to a body of code not designed for it seems like an utter nightmare most companies will forbid.
I've just created an account in dev.to to tell you that this is the best article on TDD I've seen in my life. Great job!
Wow. Thank you!
Same to you.
Really good points. Fred George mentioned something similar in this talk vimeo.com/79866979
I find myself asking the same kinds of questions when I write things like cloud functions on AWS or really small microsevices like you mentioned.
I do think there are definitely cases where TDD doesn't really help. Cloud functions, for example, allow you to set up automated tests that call the function and validate the result while it's deployed. I guess this good enough.
There's an argument against integrated tests that I love here: vimeo.com/80533536
I still find myself unit testing very small services to make sure I cover possible edge cases etc but often find I delete some or all of the test afterwards once they aren't useful any more.
I do end up working on a lot of different things like large web UI, desktop or fairly complex microservices too and getting to build very small services is still rare for me for now.
For sure. I would say though, I have been a developer joining teams that don't practice TDD or just good testing in any form and have succeeded in convincing the team to try it out. More than one team and almost without exception it has been worth it. It has just been really hard.
TDD is only a tiny aspect of the whole software life but its one that we as developers have more power to influence if we wish to.
There are loads of scenarios where TDD doesn't make sense. I am doing work on a small startup right now, prototyping. I do TDD parts that are critical or I want to design well but don't bother with much of it since it's a prototype.
It is great when you arrive at a company that is doing TDD already bit if you don't get that lucky, there is hope :) Really true comment you made though.
Just doing Google around the place it does seem as though TDD has advocates who are perhaps a little more extreme in their advocacy than would otherwise seem... well... sane. There does seem to be a religious aspect to advocates of TDD that one doesn't seem to find among test-later waterfall-type developers.
To be fair I'm new to TDD though I've heard of others using it and I've been asked about it over the years. I've been writing commercial software since 1978 and I've seen these concepts come and go, never with testable claims being made, always with generalities of "this will benefit you" either by claiming shorter time to product completion or better quality control or cost reduction and yet never have any such proposed concepts of software development in the past 40 years that have come and gone stood the test of time.
TDD looks to me to be just another unworkable, even untestable concept. It's like "Agile" which also carries a religious following with no actual science-based, proven benefits.
HOWEVER: I'm willing to see if TDD actually does hold benefits. :) It's worth at least taking a serious look, seeing if it helps quality, reduces time to develop, and produced better code. IF IT WORKS, then it's certainly something I will want to add to my bag of tricks.
Becoming a better programmer -- even at my advanced age -- is always something to work for.
Thanks for your article. You put in a lot of efforts!
I personally like TDD, I think it's a very useful tool. And yes, it does require a lot of mental strength, but I think only because we don't accept to follow it all the time.
I mean there are certain things you simply don't do. You don't go and hit people who you don't agree with. You don't throw waste on the streets (some do...). And you don't have to make serious efforts to do so, because you learnt that's the way of doing things.
Could that be the case for TDD too?
Do you mean, TDD would be a less effort if we accepted it as a good way of doing things and everybody learned it from early on?
If so, I think yes. I do believe if TDD was the norm, we might be a lot better off. I believe it intuitively but can't prove it :)
We also must consider the role that Jenkins plays performing regression testing of code bases, ensuring that everything compiles and ensuring that every automated test passes during the night when developers are presumably at home asleep.
I don't know anything about TDD, I'm only just now looking at it, however I'm looking at TDD with Jenkins firmly in mind. I'm not at all convinced that TDD holds realizable benefits yet simply having a regression test platform in place, working, and actively examined in the morning to detect faults promptly is a proven benefit.
How TDD fits in to a model of Jenkins regression development cycles is a mystery to me as yet.
I mostly agree with you, I just have a question :)
Don't you consider unit testing a good tool against regression and not only a tool to understand the code itself ?
Thanks for your great work on sharing tdd considerations! You rock!
Good topic.
It works on my machine!
Ha! :D
TDD? Well most doesn't even write tests, I am one of them and I suck at my job.
Pun intended.