Wednesday, March 21, 2012

A Day in the TDD Zone

What makes a great day of test driven development?
  1. A good plan.  This is going in my business layer, that is going in my data layer, and I'm not going to think about it just yet.  Let's wrap it in an interface and set it aside for a bit.
  2. Good tools.  NUnit of course, and a good mock suite.  Moq was rocking for me--built around lambda expressions, it makes dynamically created objects obey intellisense.  Pretty nifty.  And ReSharper is wonderful, letting you define methods and classes in your tests, and then Alt-Enter them into existence.  Control-U+Control-U to rerun your last test, and a nice 100% code coverage report for the class under test when your done.
  3. A good rhythm.  The power of the 5 minute Red/Green/Refactor cycle is that you are focused on a specific mindset.  If you are writing a test, you are not thinking about refactoring.  If you're exerting the absolute minimum amount of effort to get the test to pass, you're not going down a rabbit hole of redesign.  Once you get that green light, you give yourself a minute or two to do some clean up.  Then off to the next test.  Twenty iterations of this over the course of a couple of hours of development and your code gets pretty lean.  And don't be afraid to refactor out a test. When I started the session, I began with an object exists test.  Once I was on to testing functionality, that test was superfluous and was cut in a refactoring pass.  Zen focus makes TDD work.
  4. A few good patterns.  Roy Osherove's Extract and Override, where you put your external dependencies in virtual methods you can override in a subclass has worked well for my team. Another approach is Dependency Injection, where you take your classes with knowledge of outside systems (web services, logs, even the system clock), wrap then in interfaces, and pass these into the constructor.  (There's a nice explanation here.)  Now you can mock the interfaces in your tests.  For production, you instantiate the real versions of your dependencies inside your default constructor. Now your logic has an enforced isolation from knowledge of external class implementations, since it has been build entirely around interfaces.
The most striking thing about this practice is that at the end of the day you end up with less code than you expect, but you are much further along in your project.  Your code is very focused on requirements, is light on non-functional cruft, and you've given clean interfaces for other developers to code against.  And everything you've written is under test.  Nice.

No comments:

Post a Comment