As I create classes for systems, I sometimes find that writing my unit tests become extremely difficult. In most of my cases, I find that I can refactor my classes to make the testing easier. TDD is powerful and often morphs my conceptions of a given solution into something I may not have considered in the beginning.
What I like most about TDD is that if it is done properly, it greatly improves the design of a system, from readability to maintainablity, and of course testability. Even though I do find that a number of new classes arise and decisions on where to put these need to be addressed, the increased number of classes is a good thing. The evolution of the system is just one of the benefits.
In my MVP relationships, I typically pass in my data provider into my presenters. Since authoring unit tests for some of my recent presenters has become more difficult, I began looking at the responsibilities of them. With what should my presenter be concerned? Presentation logic? How to render a view? What to command the view to do? How to respond to events raised by the view? The presenter should be able to interact with the model, but to what degree? Should it know how to create certain domain objects? Save records to a datastore? Call a web service? If writing unit tests for these actions becomes too difficult, perhaps the class is doing too much.
In the beginning, we attempt to design a solution for a problem, drawing diagrams, screen mocks, ect. We think we know how to solve it, and in some cases we do, in some we do not, but either way, we start writing code. What I find is that when using TDD, the design evolves considerably into something I would not have considered initially. It is almost as if TDD is a process that we have to just let happen, and to some degree, we need to let it happen based on faith.
The recipe for success lies in the commitment to properly executing the fundamental concepts of an activity. In fly-fishing, it is the four-count rhythm of the cast, from 10-2. It is properly tying leader, tippet, flies, and droppers. It is how and where we present the fly on the water and how it drifts. This is not all, but these fundamentals are necessary. In football, it is the little things that win games. Getting into a proper stance, firing off the line of scrimmage, getting your hands inside the opponents, getting your facemask across his, seeing the ball into your hands, wrapping up your tackles. These fundamentals are so elementary that we often breeze through them, cutting corners, thinking they are not always critical, but they are.
In software engineering, it is the fundamentals of object-oriented design that are critical and often overlooked. TDD is a process that emphasizes these fundamentals, so there should be no shortcuts. Classes should adhere to SRP. Design to an interface not to an implementation. Inject your dependencies. Do not repeat yourself. Tell your objects what to do. The list continues.
It is the little things, the basic fundamentals, that improve chances for success. When a fish gets off the line, or a team loses a game, we reflect on where the breakdowns occurred. In most of those cases, it was a primitive fundamental. As you design, keep the faith that the basic principles of TDD will promote better systems, increase readability and maintainability, reduce bugs in the future, and improve quality.