Nowadays, I’d never build a production app without Test Driven Development.
The reason I love TDD boils down to one fundamental belief: if you can’t precisely describe the expected behavior of your system, you have no business touching the code.
With TDD, you first describe how the system works. Then you run the test (which should initially fail so that you can rule out false-positives), then you modify the code until all the tests pass.
In my early days before implementing TDD, I’d often spend hours staring at the screen, perplexed. My mind would go blank because I didn’t know where one task began and another ended.
How do you know when a task is finished? Well with TDD it’s simple: your test passes all the assertions. But without TDD, you’d need to manually test out each piece of code with each possible input, and it’s easy to fudge that up because we’re humans and we’re impatient.
Unit Tests are used to test individual functions while Integration Tests are used to tests combinations of functions. Unit testing strongly encourages you to break down the system into individual pure functions (“pure” meaning that there are no side-effects and the output is derived directly from the input).
In the end, TDD is awesome because it’s a natural way of converting business requirements into code- and when combined with Continuous Integration (automatic builds on each commit/merge)- it gives you the peace of mind to get some sleep knowing that the apocalypse was postponed for at least another day.