It's best for the developer to concentrate on the problem space rather than the solution space.
TDD is proactive.
No TDD is reactive.
By writing test first, you write code that is more highly maintainable than if you just wrote the code to solve the problem. Writing a class to use in production that can be tested too forces you to think in other ways than just solve the problem. Barring certain mistakes (see further down), the result will be better code.
By testing first, you are forced to develop something akin to a specification. The specification of what the code does is made clear by the test written to ensure it does it.
If I can't write a test for the solution or functionality I'm working on, then I surely do not understand what I'm about to do. If I stab around prototyping the solution without writing one or more tests for it, how do I know what it was really supposed to do or that it really does it.
You know how documentation—specifications—never seem to reflect what the product is doing or how it works because changes never seem to get reflected in documents?
Your test(s) will often begin to fail if you change the code to do something new or different.
I can't count the times my Mockito tests have saved my bacon at the end of a grand refactoring by showing me immediately how I broke something. I'm less afraid to make need changes to code that's completely covered by tests.
Robert C. Martin said:
Software is a remarkably sensitive discipline. If you reach into a base of code and you change one bit, you can crash the software. Go into the memory and twiddle one bit at random, and very likely, you will elicit some form of crash. Very, very few systems are that sensitive. You could go out to [a bridge], start taking bolts out, and [it] probably wouldn't fall. [...]
Bridges are resilient—they survive the loss of components. But software isn't resilient at all. One bit changes and—BANG!—it crashes. Very few disciplines are that sensitive.
But there is one other [discipline] that is, and that's accounting. The right mistake at exactly the right time on the right spreadsheet—that one-digit error can crash the company and send the offenders off to jail. How do accountants deal with that sensitivity? Well, they have disciplines. One of the primary disciplines is dual-entry bookkeeping. Everything is said twice. Every transaction is entered two times—once on the credit side and once on the debit side. Those two transactions follow separate mathematical pathways until they end up at this wonderful subtraction on the balance sheet that has to yield [...] zero.
This is what test-driven development is: dual-entry bookkeeping. Everything is said twice—once on the test side and once on the production code side and everything runs in an execution that yields either a green bar or a red bar just like the zero on the balance sheet. It seems like that's a good practice for us: to [acknowledge and] manage these sensitivities of our discipline.
Some best practices. An HP colleague posited this a long time ago. I'm not sure I agree in every respect. Mockito has come along since. Etc. Expect a lot of revision here.
Also, it constitutes testing something that's tested one level removed as tests execute for the calling methods. This is chaos.
A TDD approach lends itself to coding using SOLID principles.
A method or class should be responsible for one task, and one task only. This leads to smaller methods which are easier to understand, have fewer lines of code and fewer bugs and side effects.
A good unit test will test one method or one unit of work. In order for this to work well the method must do one thing well or have a single responsibility.
The Open/Closed principle, at the method level, suggests that the method you are testing should be open to bug fixes but closed to modification. If your method is doing more than it should to fulfill it's responsibilities then there is a greater chance of higher coupling and side effects.
The idea behind the Liskov Substitution principle is that it should be possible to substitute different subclasses of a class without a consumer of the base class needing to know about the substitution.
When a TDD approach is taken, artifacts such as queues and databases should have test doubles to shorten the code-test-fix cycle, which means subclasses are more likely to follow the LSP.
Interface segregation refers to creating tightly focused interfaces so that classes which implement the interface are not forced to depend on parts of the interface they do not use. This is single responsibility again but from an interface point of view, and as with single responsibility for classes and methods a unit test will be more effective when only the salient part of the interface is included in the test.
Having a cohesive interface, much like having a cohesive class, leads to a much cleaner contract and less likelihood of bugs and side effects.
Let's say you have object A and object B. Object A depends on object B to work, but rather than creating object B within object A, you pass a reference to object B into object A's constructor or a property of object A. This is dependency injection.
When coding using TDD you will often want to create any dependent objects outside of the class you are testing. This allows you to pass different types of test double into the object under test as well as real objects when the class has been integrated into an application.