Thursday, December 16, 2010

Testing Legacy .NET apps with SpecFlow

I've always wanted a decent tool for end-to-end testing of Windows applications.  But, now that BDD/ATDD is all the rage, I've become even more jealous that the Ruby crowd (the one to which I aspire) can use Cucumber to write executable requirements (to use Gojko's term).  That's right, to add insult to injury, Ruby developers don't just get a testing tool.  Instead, Cucumber allows the customer to communicate a feature by using specific examples of expected behavior.  Writing Cukes as the first step in playing a story card provides guidance for the developer to know they have completed the story and done so properly.  Like unit tests, the fact that you end up with a suite of regression tests (with many end-to-end tests) is just icing on the cake.

SpecFlow is a tool for writing these Cucumber tests in .NET.  As of version 1.3, it installs and uses the "official" Gherkin parser, so your feature syntax can be identical to the Cukes written by those Ruby show offs.  However, with SpecFlow your steps can be written in a .NET language like C#.

Right now, all the Ruby geeks (yes, you Cheezy) are yelling, "WHY WOULD YOU WANT TO DO THAT?"  Now to be clear, it is definitely possible to use Cucumber and Ruby to test a .NET application. In fact, if I were testing a web application that is exactly what I would do.  However, controlling native Windows applications with Ruby produces some testing challenges.  Again, it is possible to interact with Windows applications from Ruby (using the win32 gems, for example), but I contend that SpecFlow is the simplest way to get started doing Cucumber testing for Windows apps, especially for a .NET development team and/or a .NET development shop.  But, as Cheezy constantly points out: If your long term goal is to have a formal tester role writing your step definitions, then Ruby is a much easier language for those team members to learn and use.  Maybe IronRuby is an answer, but I'm not ready to invest in that, yet.

For the last 6 months, I've been using SpecFlow not for ATDD, but instead to test legacy .NET applications. The code fits the Michael Feathers definition of legacy code: No tests.  Certainly, I have been able to use the techniques in his book to introduce unit tests to this legacy code base, but using SpecFlow has proven equally helpful.  By writing tests which communicate how the existing features behave, I have been able to "reverse-engineer" the executable requirements.  Importantly, I can do so without any modifications to the code. This is by no means a silver bullet, but having these tests has certainly enabled me to more aggressively refactor.

In most cases, these applications are console applications or services.  More recently, I have written some tests that interact directly with classic WinForms applications.  I will post about the experience of using SpecFlow as a powerful enabler of refactoring legacy .NET code, including code examples which are re-creations of the challenges I've encountered.