TDD in Clojure, part 3 (one wafer-thin function; conclusions)

Part 1
Part 2

All that remains is to add the locations of bordering cells to a given sequence of locations. A small wrinkle is that a border location may be next to more than one of the input locations, so duplicates need to be prevented. I could write the test this way:

I don’t like that. When the test fails, it’ll likely be hard to discover how the expected and actual values differ. And I bet that a failure would more likely be due to a typo in the expected value than to an actual bug. The test is just awkward.

I like this version better:

It is a clearer statement of the relationship between two concepts: a location’s neighborhood and the border of a set of locations. More pragmatically, it’s less typing. (When I first started coding in this style, it surprised me how much test setup clutter went away. I had much less need for “factories” or “fixtures” or “object mothers“.)

Given either test, the code is straightforward:

Onward

No matter how satisfying the individually-tested pieces, the whole has to work, which is why everything ends by running at least one end-to-end test. A test like the one we started with:

Because I’m making up my test notation as I go, I’ve been running all the tests manually in the REPL. Now I can run the whole file:

If you look carefully, you’ll see that the test would fail because the locations are in the wrong order. But that’s a quick fix:

Done. Ship it!

Development order - Test-down or REPL-up?

It’s often said that the Lisps are bottom-up languages, that you test out expressions in the REPL, discover good functions, and compose them into programs. A lot of people do work that way. A lot of people who use TDD to write object-oriented code also work that way: when implementing a new feature, they start at low-level objects, add whatever new code the top-level feature seems to demand of them, then use those augmented objects in the testing of next-higher-level objects.

For I guess about a year now, I’ve been experimenting with being strictly top-down in some projects. I find that leads to less churn. Too often, when I go bottom-up, I end up discovering that those low-level changes are not in fact what the feature needs, so I have to revisit and redo what I did.

I get less disrupted by rework when I go from the top down (or from user interface in). It’s not that I don’t blunder—we saw one of those in the previous installment—but those blunders seem easier to recover from.

That’s not to say that I don’t use the REPL. We saw that a little bit in this program, when I was writing neighbors. It’s perfectly sensible to do even more in the REPL. I think of the REPL as a handy tool for what XP calls a spike solution and the Pragmatic Programmers call tracer bullets. When I’m uncertain what to do next, the REPL is a tool to let me try out possibilities. So I might be stuck in a certain function and go to the REPL to see how it feels to build up parts of what might lie under it. After I’m more confident, I can continue on with the original function, test-driven, reusing REPL snippets when they seem useful.

I don’t claim everyone should work that way. I do claim it’s a valid style that you’d be wise to try.

Notation

I’m something of an obsessive about test notation, and I’ve been endlessly fiddling with a Clojure mock notation. I implemented its first version as a facade on top of clojure.contrib.mock. As I experimented, though, I found that keeping my facade up to date with my notational variants slowed me down too much, so I put the code aside until the notation settled down.

I’m pretty happy with what you’ve seen here. Are you? If so, I may start on another mock package. I’ve got the most important parts: a name and sketch of a logo. (”Midje” and someone flying safely between the sun [of abstraction without examples] and the sea [of overwhelming detail].)

Tests and code together

You can see the completed program here. It mixes up tests and code. I’ve tried that on-and-off over the years and always reverted to separate test files. This time, it’s seemed to work better. I probably want an Emacs keystroke that lets me hide all tests, though. I’d also want alternate definitions of the test macros so that I can compile them out of the production system

What next?

I’ll write a web app in this style, using Compojure.

5 Responses to “TDD in Clojure, part 3 (one wafer-thin function; conclusions)”

  1. Exploration Through Example » Blog Archive » TDD in Clojure, part 2 (in which I recover fairly gracefully from a stupid decision) Says:

    […] Part 1 Part 3 […]

  2. Today in the Intertweets (June 17th Ed) | disclojure: all things clojure Says:

    […] in #clojure, the final part. (here, via @marick) (Parts 1 and 2) — This part has some conclusions (develop top-down or bottom-up […]

  3. links for 2010-06-19 – Magpiebrain Says:

    […] Exploration Through Example » Blog Archive » TDD in Clojure, part 3 (one wafer-thin function; conc… (tags: tdd clojure) […]

  4. Nate Kirby Says:

    OK - this is great tutorial - thanks.

    I do have a few questions about this. The way I found this is that I got informed about clojure and decided to investigate. Following getting started allowed for getting a simple program running to prove that it was configured and worked. Then I looked for a TDD / clojure example to learn how to test for clojure and this 3 part blog came up. following this lead to http://gist.github.com/442435 which looks very informative, but uses namespaces. The assumption is that their is a file /clojure/contrib/combinatorics.clj that contains cartesian-product . That is an *Assumption* (@ss of u & me territory). I know very little clojure but desire to learn it using TDD, the ignorance leaves me with the need to learn ns and use to figure out the reason that the current result is

    Exception in thread “main” java.io.FileNotFoundException: Could not locate clojure/contrib/combinatorics__init.class or clojure/contrib/combinatorics.clj on classpath: (life1.clj:2)

    I will investigate this and if I figure it out I will post here - however if you know please post and inform my ignorance of ns and uses.

    Perhaps there will be other comments or even a post of my own.

    Thanks
    Nate

  5. Nate Kirby Says:

    OK - I got it to run - had to download http://github.com/richhickey/clojure-contrib/downloads and build it and change the command to reference it - but not the tutorial runs - you can expect comments form me about what I find as I try to grok clojure via this TDD tutorial

    1st comment - please explain the clojure-contrib usage to clojure neophites desiring to investigate closure via your TDD tutorial

    Thanks again
    Nate

Leave a Reply

You must be logged in to post a comment.