Let your examples flow

Should examples/tests/specs/whatever be DRY(Don’t Repeat Yourself)? I’ve been thinking (and talking and arguing) about the value of test names recently and whether they are just unnecessary duplication, but that’s the subject of a future discussion. This is about the actual content of your examples. So, should your examples be DRY?

In a word, no. DRY zealotry is a classic example of an over-adherence to Best Practices, which as James Bach argues are a bogus concept anyway. When you are writing software, including executable examples, your focus should be on clarity of intent. If the code could be any more obvious, you’re probably not done yet. And given that code is created once but read and changed many more times, it is healthy to develop a strong sense of responsibility to the guys coming along after you.

In tests, flow trumps DRY

The DRY principle says that the definition of any concept should appear once and only once in your code. This is an admirable aim in that if you have to change the behaviour of a Flooble, you want to be able to change it in one place and be reasonably confident that your change will apply consistently across the codebase. If there are multiple definitions of a Flooble, the chances are that not only will you not catch them all, but that someone before you didn’t catch them all and the multiple definitions are already inconsistent, and who wants that?

However, when you are using examples to drive your code – a process I call Coding By Example but which most other people insist on calling Test-Driven Development – there is another principle in play that I believe trumps the DRY principle. The examples tell a story about what the code does. They are the documentation narrative that will guide future programmers (including yourself when you come back to change this code in three months time and you’ve forgotten what it does). In this case, clarity of intent is found in the quality of the narrative, not necessarily in minimising duplication.

Some years ago I had my first experience of pair programming with Martin Fowler. That is, I had done quite a bit of pair programming, just not with Martin before. We were looking at some ruby code I had written test-first, and Martin asked to see the tests, “to find out what the code does”. Then he did a rather odd thing. He started moving the tests around. I had a few helper classes and utility methods in the source file, neatly at the end out of the way. He moved them up and dropped them inline just ahead of the first test that used them.

Madness! I thought – now the supporting code is all over the place! It really offended my sense of tidiness. But then I saw a pattern beginning to emerge. The test code was starting to read like a story. He would introduce these little methods and classes just before their one walk-on line in the narrative. It was quite an eye-opener for me. The test code flowed, and unfolded the story of the class under test. (I know I’m using TDD vocabulary – this was in my pre-BDD days.)

Go with the flow

The A-ha! moment for me was when I imagined reading a story book where the plot and characters had been DRYed out. Everything would be in footnotes or appendices. All the character descriptions, plot elements, subtexts etc. would be carefully extracted into fully cross-referenced paragraphs. Great if you are “reading” an encyclopaedia, not so appropriate if you want to get into the flow and find out what happens. You would be forever flicking back and forth in the book and you would very quickly forget where you even were in the story. In the words of the old joke, a dictionary has a lousy plot but at least they explain all the words as they go.

So here’s my challenge to you. When you read through a test case, or spec file, does the story unfold? Does it start by introducing the object’s most important responsibilty? Does it then introduce the edge cases in descending order of priority? If the test uses helper classes and methods are they tucked away at the end, or worse yet in an entirely different file that I am expected to “just know” is there?

Try to avoid using before blocks or setUp methods – especially in an abstract test class. Just call the method that does the setting up directly from the example. Don’t leave me to guess there might a magic method in a different class that is being invoked before the test even runs – that’s just not fair.

Also thanks to Mikel Lindsaar from the rspec mailing list for an excellent article about the perils of slavishly following DRYness.

[mikel]http://www.lindsaar.net/2008/6/24/tip-24-being-clever-in-specs-is-for-dummies

10 comments

  1. I disagree. If you’re using a reasonable IDE then navigating to the implementation of a helper method is usually just one keystroke away (F3 in Eclipse). If you need to see the implementation of the method in order to understand the test then that seems like a code smell to me – i.e. your helper method or class is poorly named and/or doesn’t capture the right abstraction.

    1. Hi David.

      Yes, you’re right. In my IDE I’m only one keystroke away from the definition of any helper method. But here’s the thing. I could describe to you that to get to the dining room you have to go through the kitchen. We all know what a kitchen is. We all have a kitchen, we’ve all seen lots of other ones. To understand this specific example it doesn’t matter what this particular kitchen looks like.

      But if I had a description of the kitchen for this example it would make it just a little more real for me. This kitchen is a narrow galley kitchen. It has storage on one side (the left), good lighting coming from both a skylight and a double window (straight ahead), and it has an electric oven with a four unit gas hob.

      If I wanted I could hit F3 to see the description of the kitchen, but I probably won’t bother – I already have a generic “kitchen” in my head I can refer to. However if the description of this particular kitchen appears just ahead of the example, it makes it a little bit more vivid. I’m not suggesting to duplicate the description every time, just to put it ahead of the first example that refers to a kitchen so that I’m not substituting my generic one.

      By reordering the helper methods and classes you can make it that little bit easier for me to be in the same headspace that you were when you wrote the example. Charles “cowboyd” Lowell, the man who taught me TDD, sums it up really nicely

  2. There seems to be a lot going on here. Stories are clearly important to people, and our cultures spend a lot of time telling them and listening to them. They are used in teaching, particularly within religion, and they help ideas stick, as per Chip and Dan Heath’s book.

    But generally it is difficult to lookup ideas in a story, unless they are broken down into chapter and verse, and even then one needs exegesis. This is one problem I’ve had with the business novels of Eliyahu Goldratt which teach Theory of Constraint. Trying to lookup the details of what constitutes “Drum Buffer Rope” is not really helped by the narrative structure. Also, narratives don’t have much literal repetition. I’m not sure what a DRYed out
    novel would be like. It would be an interesting artistic experiment in itself. Maybe one could try it on something out of copyright, like Gulliver’s Travels.

    I think you are onto something here, and I think given the concept of User Stories in Agile development, making the tests follow that flow makes sense.

    I do wonder if the non-DRY version should be generated from a DRY version, though.

  3. You should only break DRY to support readability as a last resort! Any duplication should be kept to an absolute minimum. Only when you have exhausted all attempts to improve readability should duplication be considered.

    Felix had a good post about this at the link below. To summarize – Duplicating the ‘what’ is less evil than duplicating the ‘how’. You can achieve this by refactoring to declarative Setup and Assert (sorry – Given and Then). If you haven’t already you should read the post.

    http://wuetender-junger-mann.de/wordpress/?p=582

  4. Dan,

    first of all in my not so humble opinion the DRY-Dogma has to be defended and there is no real exception. We know that test cases are prone to receive too little maintenance, so a bit of DRYness is essential!

    Hgs points to the solution of your dilemma, when he says that the non-DRY version should be generated. Wasn’t this what the StoryWriter thingy in JBehave was all about? Essentially you would like to have an execution trace. You might even improve that by annotating/ instrumenting your test code with special story output.

    If you think about acceptance tests it wouldn’t necessarily be a textual thing. You could use a screen capture tool in conjunction with selenium and actually produce a video (potentially adding special logging that get’s rendered as subtitles in the video (or refers to an audio file)).

  5. Dan,

    As a side note, I’ve considered for a while now that there are several cases apart from just testing when it helps to distance yourself from (though not abandon) DRY. I did a write-up awhile back about how I strategically violate DRY in my day to day coding, and why it actually makes my designs and my code better.

    Have a look homie, and let me know what you think.

    http://www.thefrontside.net/blog/repeat_yourself

  6. Hi Dan,

    Thanks for valuable insights. But I’m getting a little bit confused here. I read you to say that the test cases should read like stories, and that moving supporting classes and methods “just ahead of the first test that used them” achieves that. But you still DRY them, don’t you? So aren’t you actually arguing for letting the examples flow more than against DRYing? DRYing is not necessarily “moving out of the way”.

    On the other hand, how far do good, descriptive names take you so that you don’t even need to move the definitions up-front in order do understand the story? Why didn’t inlining self-descriptive calls do?

    1. Hi Eivind. You’re right – there are two separate ideas here and I didn’t clarify them very well. Firstly the idea of putting helper classes, methods, etc. in the flow of the example code just ahead of the example that uses them (rather than at the end or in separate helper files). Secondly deciding that duplication between examples is ok if you consider that each example should read as a reasonably self-contained narrative.

      This second point creates a natural tension between DRY and readability. In other words you could do the first one and still end up with examples that were too DRY to read easily. The second issue is the more important one for me – especially if your testing/example framework executes potentially hidden methods that alter the flow of the example.

  7. NNUG Bergen in August…

    August is traditionally the startup month after the summer for NNUG activities and NNUG Bergen has really…

  8. This is an interesting point of view and i largely agree with you. But the point is a larger one, i.e. it’s not only confined to tests (although tests are definitely a ripe target for this kind of thinking).
    There are too many developers who subscribe to the “DRY-Dogma” as felix puts it above (apparently without irony). It’s part of a larger morbid epidemic-developers who can’t think for themselves

Follow

Get every new post delivered to your Inbox.

Join 456 other followers

%d bloggers like this: