TDD is BS**
** BS = Behavior Specification
*The misleading terminology around TDD is responsible for a good amount of confusion and hesitation in this area, as well as over- and undertesting. Calling tests specifications makes the concept more intuitively available in several ways.*
testing thin air
One cannot test something before it is built. Asking people to write tests before they write code is like asking them to test-drive a new car before it even exists. This isn't possible. Similarly, a piece of code can only be tested after it is completely written. We can't test thin air.
Asking people to write tests before they write code is counter-intuitive and misleading. Resistance or discomfort with the idea is only natural.
specifications instead of tests
A better perspective is to treat tests as specifications for the application in the works, i.e. descriptions of what our code should do.
It is completely plausible to reflect upfront about what I'm going to build - and to write down these thoughts - before starting to build anything serious, costly, and/or sizeable. Especially when working within a team.
It is similarly intuitive that starting to think about these specifications - or writing them down - after a product has already been built makes very little sense, provides very little benefit to the development process, and is actually more a hassle than useful at that point. Specifications need to be defined, agreed upon, and considered before we build the actual system.
Specifications just define expected behavior. They don't dictate how in particular to implement things. The idea of "testing", on the other hand, naturally means checking a particular implementation in all its gory details.
This distinction is important. Ideally, our tests/specs should not be concerned with implementation details and private methods. Otherwise they become brittle, and will break unnecessarily each time we refactor or clean up something. All that we really care about when running our specs is that the code still works, not how it still works. The how should be discussed in code reviews.
Developers tend to occasionally forget about the user perspective while being deeply immersed in the code. Specifications give them a natural place to muse over the user-visible behavior of the application, from the perspective of the user of their code. Specifications thereby form the missing link between business requirements and their technical implementation. Features specs (aka "integration tests") are runnable user stories.
Tests, on the other hand, just assert implementation details from a technical perspective. Even integration tests just check that the different parts and layers of the application have been integrated properly.
While there is often no real technical difference between "tests" and "specs", the term "test-driven development" is misleading, while "specification-driven development" is intuitive and describes what we actually do here better. We should consider using the latter term in favor of the former. Let's write "specs" instead of "tests", and "feature specifications" instead of "integration tests" from now on.
Much love, and happy coding! :)