On testable code and TDD
Ok, so I haven't blogged in a while...
But I think I've learnt a few things over this period. For a while, I think I was enamoured with writing testable code, and I took it too far. So much so that today when I look at my code, there are mostly dependencies being injected through the constructor everywhere.
Well, isn't that a good thing, you might ask?
Not necessarily. Such code tends to get defined by its static struture if you approach all your development this way. Unless we look at behavior more than overall structure, we will not get the right abstractions in place. When designing classes, the question to be asked is this: which classes does this class need to talk to?
Identifying abstractions followed by collaborations seems to be the most dificult part when designing OO software.
Another idea that I have been trying to learn is that of TDD.
Writing tests is not enough. Till now, all my efforts were towards refactoring existing code, to make it more flexible. By injecting dependencies and defining test versions of the same classes, I thought I would be able to crack the design issue everywhere, and I was way off target.
Again, I was not exactly on the mark when injecting dependencies either. Till now I had no idea of the mocking frameworks that people use to ease their unit testing efforts. I need to look into the differences between state verification and behaviour verification.
Apparently, the right way to go about writing code is in the exact reverse of what I have been doing till now : you write your tests first, and then the code follows. The rationale behind this approach called TDD, is that if the tests are written first, you only write enough code to makes your tests pass, thus staying away from the "big design upfront", which may or may not materialise, and more importantly which will perhaps not be required. The test code will be considered your specification. If the client asks for a feature, then you must have a test for it. Looking at in reverse, everytime you get a bug reported, step one must be to write a test that can reproduce the bug. Only then do you work to fix it.
Tests are also considered as doumentation. Some TDD practitioners will even refuse to look at your documentation, the first thing they look at is your test suite. Their logic: documents may lie because developers don't often keep them up to date, tests are the real deal.
They follow the RED-GREEN-REFACTOR cycle. Write a failing test case, write enough to make that test case pass. And then refactor like crazy. The aim of refactoring must be removal of duplication. And trust me, Kent Beck's book (Test Driven Development by Example) will surprise you on the topic of identifying duplication when taking small steps.
I still don't understand what they call "interface discovery", this is what makes TDD an approach to design, and not just a process that ensures your test cases get written. Another area that is fuzzy to me: how much of design is required before you can start TDD? Do you approach the screen with a blank mind, taking it from there? Or do you think a few things out before you start tapping away?