Listen to this post
When first adopting TDD, developers can run into some roadblocks that seem to indicate that TDD is a difficult process. In truth, some of these problems actually indicate faults in the system architecture.
For example, developers will struggle to write unit tests of behavior that is embedded in a user interface, or in stored procedures in a database. Allowing a test to “push a button” in a UI involves the user of complex tools and tends to slow down the test execution. The same is true if the test has to spin up the database.
Another example is testing application logic when it is peppered throughout with framework calls or calls to web-services or other external dependencies that are difficult or impossible to control during the test run.
This is a big subject, but the bottom line is this: TDD encourages a clean separation of business logic from other layers of the system. This is a good idea anyway; it means that the same application can be deployed using different UI’s (web vs mobile, for example), or using different technologies for persistence. It promotes the re-usability of business rules without redundancy.
Bad design is hard to test. So is a weak architecture. TDD helps you to see this early.