28 05 2014
Myths about TDD
Maybe you’ve seen that in the last few weeks there have been some debates about TDD. It all started with David Heinemeier Hansson(@dhh) who said in a blog post that TDD is dead because it mostly does design damage. I saw the debates on Google+ about TDD, and I have gathered 4 myths about TDD, told by @dhh himself.
1. Code coverage
@dhh says that tests don’t cover every situation, so they’re useless in the real world. Well… I thought we were talking about Test Driven Development, which requires to write TESTS BEFORE CODE, not TESTS AFTER CODE. The difference is that when you’re writing tests before code, you try to think at all the bad inputs and outputs that a function might have. It takes some time, but it’s the best way to reach a better code coverage. Writing tests after the code is in production, makes you lazy. You tend to write a test for the sad path and one for the happy path. It’s not enough, but you just want to get it done and reach a good code coverage as soon as possible.
Even if you’re writing tests first, you may miss some cases once in a while, but a good code coverage means that you will have less bugs. Yes, that’s right, I said “less bugs”, I didn’t said “no bugs”. Unit tests that pass suggest that your code is ready for production, but the product is not ready until the QA takes a look at it, and this takes me to the following topic.
2. TDD replaces QA
This is so wrong. First of all, I know that you can’t cover everything with TDD, for example, there are no unit tests for the UI. This is where QA has an important role. They play with your product and try to make it crash or try to discover unwanted behaviors. Developers should not waste time with this. I’m a developer, I write code, working code, clean code, this is my job, but there are many things that can go wrong and are out of my reach. 90% of the problems I have at work are because of poor communication. Sometimes, the client changes something in the design, I’m not notified, and when I finish the sprint, the product receives bad feedback if something is not updated. QA can stop this from happening (although SCRUM could also prevent this, but I’ll leave this for another post).
My opinion is that having a QA team doesn’t mean that TDD fails, or that the developers are not writing good tests, it just means that you want to be sure that you ship a product as close to perfection as possible.
@dhh thinks that isolation is a side effect of unit testing. I strongly disagree with him. For me, the purpose of isolation is to help you change your code faster and easier. It’s not a side effect, it’s something that I would do even if I’m not writing tests. Sure, in a perfect world, where clients don’t change requirements, we don’t need isolation, but do we live in a perfect world? Hell no. Even the clients don’t know what they want, so you have to be ready to change everything in any phase of the project.
Now let’s say that I’m wrong, but does the Single Responsibility Principle ring any bells? When did we decided that SRP is a mistake? Let’s just throw everything in one function, call the API, update the database, change the UI, etc… we’ll call the function ShowNiceAnimationWhenUserInfoIsRetrievedFromTheAPIAndTheDBWasUpdated();
It doesn’t look good, right? Maybe we’ll just call it DoStuff(), and if we have more functions in the same class, we’ll have DoStuff1(), DoStuff2(), etc. Come on people, it’s programming 101, there are plenty of reasons why we don’t do such things anymore(google them), but I think I made my point about isolation so I won’t go into further details.
@dhh says that TDD is all about fast tests. Basically, “there should be a balance between tests and the design of the code, but TDD makes you want to make your tests faster and faster, mocking and isolating everything you can extract from your code, and this will ruin the design“. This is not true. I don’t care about speed. You don’t isolate code in order to mock it, you isolate code for the reasons I said above.
It’s the same with mocking. I don’t mock my database in order to make the tests fast, I mock it because I don’t want my tests to depend on the database. I want to run my tests anytime, anywhere, and if it’s possible, I want them to be fast. If you don’t mock, then you need an internet connection, you need a 100% working API, you need a database, etc. I’ll say it again, we don’t mock because we want speed, we mock because it’s the right thing to do.
Let me make something clear, these “myths” could become reality. When I started to practice TDD in my projects, all I wanted to do was to reach a higher code coverage, and I was testing everything I could get my hands on. I was very, very wrong, my tests were very coupled from my implementation, and each change in the code would require changes in the tests. When I realized my mistake, I didn’t blame TDD as @dhh does, I knew it was my fault. Now I know that I have to test only public methods, I don’t have to test the details, because they change very often, and I shouldn’t care about them.
Everything can cause problems if it’s not done properly. Should you give up on programming because you have a bug? Of course not. TDD is not a religion. Nobody is saying that TDD is the only way to write good code, so please stop judging us like that. I’m open to new stuff, and I will try to find better ways to help me write good code, but for the moment, TDD WORKS VERY GOOD FOR ME, and I’m proud to call myself a Test Driven Developer.