So I have being doing TDD for years now.
But I started noting I tend not to do it exactly "By The Book".
So as an exercise I tried going back to doing it...
* Very fine grained steps.
* Small change so Test fails - commit.
* Small change so Test passes - commit.
* Refactor so test passes - Commit.
As small as I could.
Oh, and just for the fun of it, I'm learning the D language, so I did it in D.
So here is the result, complete with a blow-by-blow Mercurial Repository of every step.
?Conclusions from the exercise.
- Very fine grained steps resulted in fewer bugs and fewer backtracks.
- Very fine grained steps resulted in better factored code.
- Watching
others do the same exercise led me to the attitude "adding more test
cases is Bad". (Slower, hard to maintain tests, more lines of code)
- Very fine grained steps slowed me down compared to my more usual somewhat loose style.
- Thoughtfully adding Provocative test cases in the order of "What I need to Handle Next" is Very Very Good.
- Replacing
"Fine Grained Steps" with, whenever stuck, pull out a smaller? chunk
and TDD that first (with Provocative test cases) works Very Very? Well.
Bottom Line?
In future I will be doing...
- Thoughtfully choosing a minimal number of provocative test cases in the order that challenges a single new behaviour, ie. A test case that requires a single additional behaviour in the code.
- I will take the largest steps I can where I can go from test fail to test pass in a single change.
- Whenever
I misjudged and take too large a step, I will factor out a? smaller
function and test and develop that first via the same process.
- Factoring out works best like this...
- Revert to last passing state, leaving failing test commented out.
- Copy pasta the code you wish to factor / enhance into a small function leaving original function untouched.
- Get tests for new function to pass.
- Wire new function into parent cleaning out original copy.
- Get tests to pass.
- Reenable commented out test... hopefully it passes now.
What do I mean by provocative test cases?
For example, when converting Roman numerals.
"I" is provocative.
"II" is provocative.
"III" is NOT provocative, it merely is extra Lines of Code (and hence a liability).
"IV" is provocative, but a bad choice for next test case as it adds two new behaviours at once.
"V" is provocative and adds a single new behaviour.
"IV" is now provocative and adds only a single new behaviour.
?
Conclusions about D.
?I love it.
?It is C / C++ with the worst stuff removed, and the best stuff stolen from it's competitors.
?If
you are serious about industrial grade, high quality, defect free,
efficient programming, you urgently need to be looking at D as your next
language.
?Sigh! Unicode.
?That's not D's fault.
?D
handles Unicode remarkably smoothly, but ye ancient 7bit ASCII is
always going to be faster and conceptually easier than variable length
code point UTF8.
?It took a bit of time and effort to get my head around D's template system (richer, safer, better than C++)
?It
took even more time to get my head around Ranges, (again, richer, more
flexible, safer than C++ iterators, more efficient than generators,
coroutines or enumerators.)
?The choices D has made are rich in very powerful, very efficient implications.
Possibly the most striking is Compile Time Function Evaluation.
If it can be evaluated at compile time.... in D it will be.
And that is startling powerful.
--
John Carter
Phone : (64)(3) 358 6639
Tait Electronics? ? ? ? ? ? ? ? ?? ? ???
PO Box 1645 Christchurch
New Zealand