Every bug exists because of a missing test in a system. The way to fix bugs using TDD is first write a failing test that represents the bug and then fix the bug and watch the failing test turn green. This not only fixes the bug but it ensures it will never come back because you have a test in place that will fail if anyone makes changes to the system that produce that bug.
Bugs in software can be grouped into several different classifications. We have syntax errors or typos, and we have more serious problems like logic errors or design flaws. Test-driven development addresses all of these issues.
Tests provide a level of feedback the compiler can’t. Traditionally, the only feedback developers got before running a program was the syntax checker in the compiler, which checked that your source code made syntactic sense but didn’t check that it made logical sense. Compilers can only check syntax and a few basic kinds of errors. They can assure that code is well formed, but that doesn’t assure that it’s logically correct or that it does the right thing.
Unit tests bridge that gap between syntax errors and conceptual errors. They catch mistakes no other tool can. They catch mistakes you might have missed but your customer would find. I would much rather have one of my tests fail than get a call from an upset client. I don’t write perfect code—no one does—but I prefer that those blunders stay between me and my unit tests.