Thanks for posting
https://github.com/testdouble/contributing-tests/wiki/Detroit-school-TDD
Its a really interesting comparison of TDD schools. I didn't realize there were both London and
Detroit camps.
In section 19.4 of the A Philosophy of Software Design, 2nd Edition book, I was surprised to read,
"“Test-driven development is too incremental: at any point in time, it’s tempting to just hack in the next feature to make the next test pass. There’s no obvious time to do design, so it’s easy to end up with a mess.”
I thought, "I'm not sure if John actually learned how to do TDD in the first place"... maybe
this is just an accidental strawman, from lack of good information.
In my practice, which maybe veers more towards Behavior Driven than Test Driven,
the design happens primarily when first writing the test. While classic Detroit TDD
says only do it during Refactor, I find starting so myopically suboptimal. To elaborate
on my variation/different approach to TDD, arrived at through years of practice:
I think of tests as the "clients" of the implementation "server". My mantra is, "write the client first". This means
I am designing the external client interface before writing the internal server
implementation for it. This has two advantages:
1. the need to refactor in the red-green-refactor cycle is minimized. This is
nice because refactoring is has no (Skinner operant conditioning) behavioral
reinforcer, and so we as humans are conditioned to omit it.
2. The superior operant conditioning of writing tests (clients) first is preserved.
The major revelation/value I derive from TDD/BDD is self-managing my own
development behavior. And, design has happened.
Consider:
If you write the test (client) first, you are proposing a design. But you have
an actual implementation of that design in the form of the test, which is
runnable and red at first, which means you have validated the design partially
but immediately--if you can't translate your idea into client (test) code, it
really has no chance of working.
Now you write the implementation (server) that is driven by that test client.
You are free to experiment, even make guesses, about what might satisfy
both the new test and the full set of existing tests. This freedom allows
discovery and evaluation of (server implementation) approaches that work;
and rules out those that cannot under the existing set of rules (the test suite).
I cannot begin to describe how delightful it is to "take a stab", "a wild guess",
a "leap of faith", or "this is so crazy, but maybe it will work?" and actually have
it turn out to make the test suite all green. You want to shout from the
mountain top "Hurrah!!"
Here was my other big insight: the major advantage compared to the reversed procedure (server then client),
is operant conditioning based. The implementation making the test go green
is a major reinforcer. I've just reinforced the chain of: test-implementation, and
now I have a one-test-bigger test suite, along with a server that has the new capability.
In contrast, doing server first then client (traditional unit testing) results in writing
tests being operantly punished. You "think" you are done when you've got the
first server implementation compiling. It is very "off putting" -- it is "a drag" -- to realize now you
have an additional step! Drat, now I've got to write the test. Grrr. In TDD, you actually
_are_ done(!).
As we know from Skinner, things that get punished, by definition,
decrease the frequency of the behavior. Hence nobody tends to write tests when
doing server/implementation first. It requires alot of discipline to fight our
operant condition-able natures.
It is much better to leverage rather than to fight human nature.
The other big insight is how best to teach TDD. As I observed above, TDD can
easily get strawman critiqued when it is not understood.
When I took a TDD class to learn the approach, the instructors were very good at
again leveraging operant conditioning principles. They "back-chained" the behavioral
sequence that is practiced in "red-green-REFACTOR".
That is, THEY TAUGHT REFACTORING FIRST. This is the most efficient way we know in
operant conditioning to teach longish behavior chains. Start from the end. Get
the end (refactoring/design) fluent, then add the next-to-the-last step. And repeat.
Refactoring is the design step, obviously. So in "properly taught" TDD, or at
least how I teach and practice it, design is both taught first, and practiced first
when following my mantra, "write the client first".
Best wishes,
Jason