Just some random thoughts from Scott Bellware’s TDD event in Houston yesterday…
Something that I hear a lot of TDD newbies and skeptics complain about is doing things in code for no other reason than to enable automated unit testing. My take on the subject is to just get over it. Anything that makes it easier to write your unit tests makes it easier to make your code work.
A subject that I go back and forth on in my own mind is opening up methods or members on classes for no other reason than for testability. It definitely feels icky. When there is an internal step in a class that is complicated enough that it needs to have a unit test all by itself but doesn’t really deserve to be part of the real public interface I might pull that step into another class or make a public static method for that step if it’s relatively stateless. If I do just open up the intermediate member I usually rationalize it by not including this step in the abstracted interface that other classes depend on. For example, I might create a method on a ConcreteService class for PerformIntermediateStep(), but that method would never be a part of the IService interface that ConcreteService implements.
There were a couple questions on how to enforce TDD practices throughout a team. There was also some discussion on how a developer knew his unit testing was robust enough and considered all of the cases. One way or another success still falls into a question of the skill, work ethic, discipline, and experience of the developers doing the work. That’s why we’re paid the big bucks.
J. Sawyer, our MS evangelist in the Austin area, shared the best idea for a shame card that I’ve ever heard of. In J. Sawyer’s version you have the shame shirt throughout the project. When you break the build you have to wear the shirt. The kicker is that the shirt is never washed, so it’s quite a punishment by the time you get a ways into the project. I think we’ll pass on that though. I’m having flashbacks to my high school football days and it’s not a pretty (smelling) memory.
Whatever your coding standard is, make it easy to follow. If you have to go out of your way to do some sort of exact spacing or code format I think you’re just wasting time. Learn to tolerate the default formatting in VS.Net and set your naming preferences in ReSharper and just let the tools do the automated formatting. I honestly think that your entire coding standard should fit on a single wiki page without much scrolling. I want to be able to just go “CTRL-F” and get my code automatically formatted. Let the computer do the grunt work formatting so the human beings can do the thinking. Otherwise we might get Skylab and the Terminators running around.
Writing that first unit test can be the most difficult task in TDD. We’re taught that to be successful you need to deal with the riskiest, hardest tasks first. I agree, but within any given TDD coding session starting with something easy to do first is a perfectly acceptable way to get going. Once you have some working code it often makes it easier to “see” the rest of the code.
Scott Bellware had a thoroughly humorous rip on the “Executable UML” will o’ wisp* yesterday, but UML in small dosages is not evil. If you don’t know what or how to write the next unit test, a bit of UML sketching can be an effective way of determining the next unit test to write. Too much UML can easily lead you astray with early complexity, but writing the tests first can clamp down on overdesign. I still think Responsibility Driven Design with or without CRC cards to be more effective as a supplement to TDD than UML, but your mileage will vary.
Doing TDD in Visual Studio.Net without ReSharper (insert favorite refactoring tool here) shortcuts and templating is painful. Watching Scott perform an introductory exercise on TDD while purposely forgoing any ReSharper magic was excruciating. ReSharper definitely makes TDD faster by removing friction and automating purely mechanical tasks.
Houston traffic sucks.
* Thank you Scott for pointing out that Executable UML requires a great deal of OCL markup, i.e. code