As some of you might know, and this blog will attest, I've been talking about doing open source work with FHEM for some time now - around 3 or 4 months now I'd guess. As of yet, nothing's been published, and this isn't good. I'll be honest, there's not been a ton of work going into this project (around 5 to 10 hours a week), but still, 3 months and nothing to show for it? What the hell!?
When I think about it, there seem to be two big reasons for this this, the first I'm going to talkabout in this post, and the second in an upcoming post. Where my testing problems are concerned, to spoil the ending a little, it does involve BDD with Specflow, but we'll come to that.
TDD (Test Driven Delusion)
The first few weeks were all good - I was just getting into TDD, and this would be the first project to use it in anger from the start. People warned me of some of the pitfalls that were coming, particularly around architecture, but I just ploughed on. In my mind, the mantra "red, green, refector" - I'd read a few of the TDD katas, it looked pretty straighforward.
My first test, the little bar goes red. I quickly plug in the bare bones of my class under test with canned responses, the test goes green. This is the only class in my project, so the refactoring's simple - tidy up a few names here and there, done.
The second test goes in much the same fashion, it's so quick, everything seems to lucid, I'm getting some good feelings about this code - it'll be solid, reliable, it'll just work and I'll never have to worry about it again (Telnet doesn't change, right?). Sadly as subsequent tests were to prove, this feeling wasn't going to last.
Lean? We don't need no stinkin' lean!
My next class has the responsibility of getting devices into and out of my telnet connection.
This class needed a little more work to setup and test - suddenly I have dependencies to mock, and I have to get all the canned responses prepared for my mocks. I'm looking at the XML data that Fhem is giving me and I start using XPath to pull out the fields I'm interested in, I notice that I'm actually writing a basic XML-to-object mapper.
Hey - wouldn't it be cool (alarm bells should be ringing at this point...) if I created a generic XML to POCO mapper using lambdas and XPath and fluent notation? Bear in mind this potential for awesomeness has popped into my head about 1 or 2 weeks into the project. This idea then proceeded to mushroom and soak up the proceeding two months of the project. Finally I take a step back and realise I don't actually need any of this functionality yet, and I end up ripping it out of the solution.
Don't get me wrong, I don't totally regret this two months. After all, isn't one of the great things working on open source that you can take the time to do things RIGHT? I've stashed this work to one side, so if in the future the app drives me to need this flexibility it's there.
To sum up the problem - when I've practiced TDD to date, I've worked in a bottom-up fashion. I TDD the parts of the repository layer I think I'll need, from these I test and create the next layer up, and so on until I've got a feature that I'm ready to skin a UI around. But this leads to a lot of playing around in these layers which may or may not actually make the cut. It may be driven, but it's not exactly directed.
Behaviour Driven Development To The Rescue
I need some constraints - I need something that forces me to continually be looking at the end-user functionality I'm working on, and something that will stop me getting carried away with adding needless breadth to my lower application layers.
To help direct my testing, I've adopted SpecFlow and Selenium WebDriver to practice Behaviour Driven Development (BDD) on my UI. I create a SpecFlow feature, describe what the application should do, and when I have a failing test I can practice traditional TDD to build up the layers beneath to make the SpecFlow tests pass. Since I'm continually working towards a clear user-level test, I can focus on doing the minimum work required to get this feature delivered.
I'm also wondering whether class TDD should even be done in a bottom up fashion? Is there such a thing as classic TDD anymore, or are BDD and TDD such close offspring that they can be seen in code as the same thing? Still lots of questions!