If you’re not practicing TDD, your code is wrong. If your code happens to work without tests, then it’s still wrong.
What is TDD? At it’s simplest, it’s test-first. That is, write down what you expect the code to do, then write the code to get the test to pass.
Why even test? Surely just manually running the code is enough to see that it works, right?
No. Tests provide automated and repeatable use cases for the code. Without them, to get the same quality of code, you need to write down exactly how to verify the code, and then follow that procedure each time the code (or one of it’s dependencies) changes. Compounding that with the other parts of the code, this eventually presents a mountain of work to do just to even verify small changes.
With automated, repeatable tests, the only difference is that the verification procedure is written in code. This allows your computer to follow those steps, which it can do in orders of magnitude less time than you can, with much higher attention to detail than you can continuously give it. Additionally, it allows you to more tightly control all the inputs and outputs, so you know precisely what caused a bit of code to go wrong.
Additionally, anyone else who works with you now has a simple script they can run to verify that your changes work, instead of having to look up and follow your documentation to try to figure out what you did to test it. This can even be generalized into an external environment that automatically runs the test script to determine whether or not your changes are good - something which is called continuous integration
So, testing has it’s values, sure. Why test first? Why is that so much better than writing tests after the implementation code is written?
- It forces you to write down, in code, what you expect the implementation to do. Writing this down will also force you to write down branches of the code as it moves through.
- This bypasses the whole “yeah, we ran out of time to write tests” issue - always write tests, even when something like a time crunch makes it painful.
- It’s much more scientific.
TDD essentially applies the scientific method to programming.
- You take the observation (what the code should do)
- You take the hypothesis (what the code is now)
- You write down tests to verify the hypothesis against the observation
- You continuously run those tests against, modifying the hypothesis until it matches the observation.
- It’s more relaxing.
Once you’re in the mindset of “the code is done when the tests pass”, this becomes more like a game to get the tests to pass.
This generalizes to me being a big fan of rspec-based testing frameworks. I find that this better allows me to express the branching behavior of tests, as well as makes it more obvious the different effects a given action (method or function) can have.
Bryan Lile’s TATFT lightning talk expresses a lot of the same philosophy that I do.