Teaching TDD (TTDD)

There has been a flurry of discussion about how to teach TDD, sparked off by a recent post from Justin Searls. In it he lists a number of failures that range from “Encouraging costly Extract refactors” to “Making a mess with mocks” all of which distract attention from the concept that “TDD’s primary benefit is to improve the design of our code”. He concludes by suggesting that once you have written a failing test, rather than get-to-green in the simplest way possible you should “intentionally defer writing any implementation logic! Instead, break down the problem by dreaming up all of the objects you wish you had at your disposal”. In essence, design the elements of the solution while the first test is still red.

RedGreenRefactor

It’s an interesting post that raises a number of issues, but for me its value lies chiefly in opening the subject up for debate. The introduction is particularly pertinent – just setting a class a bundle of katas to do does not, of itself, encourage learning. The pains experienced while doing the exercise need to be teased out, discussed and have alternative approaches described. If you don’t hear the penny drop, then it hasn’t dropped.

Pitching in with characteristic vigour and brimstone came Uncle Bob with a robust rebuttal containing both heat and light (though some have been put off by the heat and never got to the light). Bob makes some good points regarding the fallacy of writing tests around extracted classes, the tool support for extract refactoring and the central place of refactoring in the Red-Green-Refactor cycle.

By the conclusion, however, Bob has switched tack. He states that while refactorings are cheap within architectural boundaries, they are expensive across them. Whether he’s right or wrong on this point* is of no concern because now he’s addressing the wrong question. The question at hand is “how is it best to teach TDD?” and it was taken up in a short Twitter exchange between me, Kevlin Henney and Mike Long.

Mike, being hard-core, says that he teaches TDD by “start[ing] with writing the test framework. Start from assert and up.” There’s much more to discover about this approach, and it’s certainly reminiscent of how Kent Beck learns a new language (by reimplementing xUnit).

Kevlin, in an e-mail, is more discursive. He works with a mixture of prepared material, discussions, instructor-led demonstrations and learning exercises. There are katas in the material, but there’s lots of interaction to draw out the key points and really get them to stick.

In my TDD training, I start by focusing on fundamental unit testing practices. The key word here is ‘fundamental’. TDD is unit testing++, so you need to have a firm grasp of what a good unit test looks like. We work through a series of simple bank account examples (in pairs, using Cyber-Dojo of course) that bring out the 6 essential properties of unit testing. I then use an example (based on an idea of Rob Chatley‘s) to introduce test doubles before moving into one of my own legacy code exercises. In-between I use a couple of the katas that ship with Cyber-Dojo – usually LcdDigits and PrintDiamond – to get a more varied domain experience.

There are lots of ways to teach TDD and Justin Searls has certainly identified one way of doing it sub-optimally. I have to disagree with his conclusion that the fix for this is to defer implementation till after the solution design is fully sketched out. At the opposite end of the spectrum is Keith Braithwaite‘s “TDD as if you meant it” which is an exercise you can (and should) try at home.

In my opinion, the success of any training is dependent on the trainer – the material is of secondary importance. So if you decide that some TDD training is for you, remember to think about who is training you, not just how long the course is and how much it costs.

 

* Grady Booch once said: “All architecture is design but not all design is architecture. Architecture represents the significant design decisions that shape a system, where significant is measured by cost of change.” Steve Tooke pointed me to an old post by Nat Pryce, which hints at a different trade off between change and cost. After all, when was the last time you were perfectly happy with an ‘architectural’ decision that was made more than a week ago by somebody else?


Posted

in

, ,

by

Comments

2 responses to “Teaching TDD (TTDD)”

  1. Carlos Ble (@carlosble) Avatar

    Nice post Seb,
    I’ve been training developers on TDD for about 4 years now. The very first editions I used to practice TDD at the projector so that people could see me and understand. Obviously they used to fall asleep soon. Now I do very little during my training courses, they do all the work and I just make some points or corrections. I code on the projector occasionally.
    Now for the first time I am training trainers on how to teach TDD and I’ve written a 30 pages manual full of “trainer notes” but I don’t have any guarantee that they will teach developers as I do. It’s scary because I want to preserve the experience that participants have during my courses but I don’t think there is a means to ensure it. Your sentence “remember who is training you” is a kind of relieve for me.

    Thanks mate 🙂

  2. Marc LY Avatar
    Marc LY

    TTDD… really nice name! T2D2 sounds too much like star wars ^_^

    As you open the discussion on the “refactoring” part… I will say that to me, there is not open to discussion while examining every facet of the process and the possibilities is essential, what I feel about TDD is that you write test that guide your development…
    Now a friend ask me not only once sarcastically: how do you test the tests?
    Precisely, you “don’t” write any test to test the tests! this is a never ending cycle anyway even though you want to do that because at some point, you will have to decide to stop write tests on your tests.
    That is precisely the reason why instead of writing test over tests, “we” write code that passes the test and make them green AS FAST AS POSSIBLE which really means AS SIMPLE AND NAIVE AS POSSIBLE! why?
    Because SIMPLE is almost auto-proven to be correct so it also prove that your test does something that is “correct”… and FAST means that you don’t break the cycle of TDD which demands to be as quick as possible.
    If your first implementation, from making the test green from being red, takes too long… then you probably break one of the fundamental rule of TDD: small steps and smallest steps if you can.
    And when you do that litterally, you prove 1) that your test can be satisfied by a small delta of code because that code didn’t take long to be written and 2) that your test is probably not a wrong test because the code you just wrote is so simple (and also sometimes naive) that it “proves” by itself that it is correct! and by extension, your test is also correct.

    Sorry, I didn’t intend to make that first answer too long since this is not the topic of the present discussion. Now about Teaching TDD…

    I want to start first by saying that, in my experience, the difficulty to teach TDD is “coupled” with the difficulty of teaching eXtreme Programming. For example, if you can pair with the learner, no problems really occur. Now, you cannot pair with a group of people so… what I personnally do is this:

    1) take a simple example of code to write (with no use of any framework nor library whatsoever): so only a function with no coupling whatsoever. And if possible, not a mathematic example… they tend to annoy a lot of people even computer scientist… don’t know why -__-“.
    1.bis) by simple example, I mean that even the unit-test code must be simple: using only assertEquals() only for example and nothing else. Introducing only the most important thing that is how you write your test… don’t teach the unit-test framework at this moment. A simple introduction with two or three phrases is sufficient. And give an already prepared template of unit-test source code.

    2) continue on few other simple example with no library nor framework uses… a function on string, an other on integers, an other on a list for example give an overview of the kind of unit-test you may do for TDD. Only by this simple repetition can the learner grasp the essence of TDD.
    2.bis) at the end, you can formally introduce the 3 steps cycle of TDD

    3) with an other example or just one already taken example can you now show the detail of each step of TDD. With pairing, all of these steps are fluid; among a group, confusions and misunderstanding can may that third step very long but it is important that every question finds an acceptable answer.
    One issue I face is that developper learning TDD always keep in mind that TDD is useless! and the question doesn’t find any acceptable answer when you face simple examples to introduce TDD… because the examples are too simple, most of the time, after an hour of TDD, the learner come to realize that he or she wrote a bunch of 3 to 10 unit-test cases and a function, and that function would have been implemented only in 1 or 2 minutes without TDD. That is the problem here and I have no way to do it than saying: “believe me, you’ll see later that TDD has a lot of advantage”.

    4) now it’s time to take a more complicated example that requires several big steps of TDD for the “final” function to be fully implemented. For example, a string-splitting function seems great but since almost all languages have a full set of string functions, the usefullness problem can rise again. So the example is somehow important at this step.
    In that more complicated step, I introduce the whole TDD idea not only the 3 step cycle… and bring some idea about “clean code”, “refactoring”, “design” and the importance to “make it past quickly”.
    I particularly take care to show what “refactoring” means especially in the TDD process. This is sometimes difficult as some people really code naively for too long before they refactor when they start TDDing which make the refactoring step very messy and unclear.
    I also focus in this step on a) launching the test as often as possible and b) make the test pass as quick as possible! this is the spirit of TDD. Well, refactoring often takes a much longer time which is ok.

    5) only then when TDD is understood and unit-test is well understood as the center tool can I start introducing a) the unit-test framework if necessary (but only what is necessary for the TDD to be learned confortably), b) the stubbing, faking and mocking technique plus some OO technique like dependency injection and c) I emphaze on the way, writing test first changes the way you write you production code that is (for example), function prototype first (so Interface and API) and then implementation only, not commenting without any use (see. Clean Code) etc.

    6) at the end, it can be really usefull to take a “real” example coming from the learners themselves… I find that by proving on a real useful case that TDD works, only then, learners begin to really focus on it (not always all the time though).

    7) at that moment, I can redo any 6 first steps since no “blocking” question is raised. And I can continue to more pratice and/or more advanced techniques or simply developping ideas in details.

    One problem I had is that I tried to teach TDD by also introducing pair programming and I found that pair programming is too complicated at first to add it to the teaching of TDD. Is anybody is successful on teaching TDD through with pair programming?

Leave a Reply

Your email address will not be published. Required fields are marked *