Test Driven Development
By Kent Beck
Oh, what a book!
I was aware of this practice before, but I didn't want to read a book about it. After all, how much can your coding style, way of thinking about code, and general architecture change after reading a little book about just writing tests before coding? Well, it's a little more than that, but the idea is clear.
It had a strong impact on me. It made me rethink how I approach building new things, whether it's for work or open source projects. You can write tests (or rather, specifications) for any codebase. And testable design is almost always better, because it tends to make code more modular and testable.
When we write a test, we imagine the perfect interface for our operation. We are telling ourselves a story about how the operation will look from the outside. Our story won't always come true, but it's better to start form the best-possible API and work backward than to make things complicated, ugly and "realistic" from the get-go.
Oh and I also like one random comment that I've found about testing stuff:
Unit tests are the ones you write because you are afraid you will screw things up.
Integration tests are the ones you write because you are afraid of other developers and systems.
End-to-end tests are the ones you write because you are afraid of the user.
What TDD is about?
Certainly not about making a shittone of tests and making your codebase have 100% code coverage.
Should I use TDD for everything?
Some people think that if you like a certain way of building things, you have to do it the same way all the time, exactly by the book. You don't have to.
Drawbacks
There are some arguments against this practice that I would like to address. Sometimes people confuse TDD with testing in general - some are against automated testing, expressing the idea that creating automated tests will not catch bugs, and so they do no testing and rely on using a debugger when they catch a bug - which is just manual testing at that point.
▸ TDD increases code volume, adds maintenance overheads and results in higher dev
costs
The idea is that writing in a TDD way always produces more code, which ultimately adds maintenance overhead,
consumes devs time, requires a test environment, and all of this contributes to higher development
costs.
It is true that if you do TDD, then you may write more code than before because now you also have tests. However, it is in your best interest to write an API that is easier to test, easier to use and may lead you to write less code in the end. Did I mention that you don't have to do 100% code coverage?
Maintenance overhead is a trade-off since you either bring up a debugger when your code breaks and test your code this way (manual testing) or have automated tests from the start. It's up to you where you want to tip the scales.
With the time consumption part – it varies from one developer to another. I believe the creator of Clojure tried TDD and found out that it slows him down a lot, so he would not practice it anymore. So let's say it depends.
Ultimately, the "cost" of a methodology needs to be evaluated in the context of the specific project, team, and long-term goals. There's no one-size-fits-all answer. Pity, isn't it? I wish it was easier!
▸ TDD gives false sense of security from tests
This is only true if you misunderstand how TDD should be practiced. TDD emphasizes tests that define
behavior, not implementation. You start with the failing tests to guide your code, write minimal code to
pass the test, and then refactor it. So when you write tests, they should mean something-you don't write
tests for the sake of having them. Or write a failing test, fake/mock it, and forget that it exists.
▸ TDD only works for big/complex projects, there's no need for it in CRUD
applications
Meaning that you're better off investing time in removing the complexity itself and the need for testing,
and CRUD-like software is better off without it. It's invalid. Implementing TDD, even for CRUD applications,
forces better code design and a clearer understanding of requirements. The act of writing tests first forces
you to think about how the code will be used and what its expected behavior is. This leads to more modular,
well-defined components that are easier to test and work with.