Back to topics from the Elegant Code Open spaces lunch. And this was a doosy.
We were talking about getting developers to embrace TDD in their development methodologies. As it so happened, one of the developers that need to be embraced was there — which always make for a more interesting discussion.
His main argument against TDD was that it almost required interfaces to do it. Why do we need interfaces? Because in order to mock something, it has to have an interface or an abstract class (using Rhino Mocks and Moq. Sorry TypeMock, they aren’t breaking open the wallet). So to do TDD, it is almost required to have an interface in every non-domain class in the system.
Why didn’t he like interfaces? Partially because it was more work to deal with them. Using resharper you can’t use Ctrl-B to go to reference, you have to use Ctrl-Alt-B to Go to Implementation. Then you have to keep everything in sync. And if you aren’t using ReSharper it is much worse. Lets not be petty about this, implementing interfaces and keeping them up to date can be a bit of work, EXPECIALLY when the interface will only be used by one class. Now it is just plumbing.
This isn’t the only compromise we make for TDD. We also remove most private methods. We make methods private by not adding them to the interface. This becomes even more pronounced when you look at other languages (cough**ruby**cough) that don’t require some of this plumbing.
Here was my argument for interface and tdd.
First off, if you learn to use ReSharper effectively, dealing with interfaces becomes almost no work at all. There is a refactoring called “Pull Member Up” that adds members in a class to an interface. And if you add something to the interface directly, alt-enter will implement a stub on all of the classes that implement the interface. Really, it can be almost pain free.
Second, from my Head First Design Patterns book: Program to interfaces, not implementations. This is also in the GoF (Gang of Four) Design Patterns book. Interfaces are about not being tied down to a particular implementation. Make things swapable. It is also about making sure your class doesn’t care how any dependency class is implemented. You can also read these points on the Wikipedia page on Design Patterns:
- clients remain unaware of the specific types of objects they use, as long as the object adheres to the interface
- clients remain unaware of the classes that implement these objects; clients only know about the abstract class(es) defining the interface
Third, this is another way to combat code complexity. When you design an interface you make yourself think a bit more about the implementation (at least I do), think about how you want the code to be used. You don’t always get more done by write more code faster. It often pays to take a moment to think about what you are doing. When you do this you can often get away with writing less code and create a better implementation.
Forth: more time spent early on with TDD mean less time spent later in the debugger. Less time you have to spend in the debugger the faster you can release your product. Now, I’m not a 100% TDD guy. There are some things that are just so hard to test that you start diminishing returns. But Presenters, and services should be very testable.
Fifth: code reuse. I have had much better luck with code reuse do to TDD than I have have without it. Before TDD I would try to share classes around and end up having to untangle a host of classes to get the code reusable. Post TDD, much less work. With effective use of IoC containers it is almost no work.
Sixth: If you want the goodness of an IoC, stick to creating interfaces. That statement isn’t 100% true, but because of limitations in the .net framework and our tool set, this is what we have to do.
Finally, one of the last arguments against TDD that I heard that night was “but I have to ship my code on time”, as if we TDDer’s get a pass on timelines. Dude, if you’re reading this, I’m sorry for jumping all over on that one — but you deserved it.