-
Notifications
You must be signed in to change notification settings - Fork 128
Top down testing
(Video)
In Midje's readme, I showed this fact:
(fact
(numerical-reverser 103) => 301)
One way to implement numerical-reverser
would be to convert 103 into a string, reverse it, and then parse that string into an integer. String reversal is easy, right?
Well, actually, it doesn't seem to be, not using the Java API. My thought process might go like this: "There's no reverse
function for String?! Wait, there's one on StringBuffer? So I have to move the string into a StringBuffer, reverse it, then toString
it? That's crazy. Clojure's reverse
works for vectors, I see, but it gives me a seq. How do I turn that back into a string?... Man, this is not what I want to be thinking about right now."
What I want to do instead of that thinking is what
Abelson and Sussman dubbed "programming by wishful
thinking" in their masterpiece Structure and
Interpretation of Computer
Programs. In
that style, while writing numerical-reverser
, I'd say to
myself "I'm going to assume a function
string-reverser
exists. That'll make it easy to write numerical-reverser
. When I've finished that, I'll implement string-reverser
if it happens not to exist. Long journeys are made from small steps."
The Midje fact that makes that coding test-driven would look like this:
(fact
(numerical-reverser 103) => 301
(provided
(string-reverser "103") => "301"))
And the code could be written like this:
(defn numerical-reverser [n]
(String/parseInt (string-reverser (str n))))
Actually, Clojure rips you out of the lovely world of wishful thinking a bit early. Because string-reverser
doesn't actually exist, you have to declare it to keep the compiler from spewing a huge stack trace at you. I like predeclaring wishful thinking functions like this:
(unfinished string-reverser)
That's the same as declare
, but it fails more informatively if it's ever called.
In the object-oriented world, this style is well described in Freeman and Pryce's Growing Object-Oriented Software, Guided by Tests. My original goal for Midje was to repaint that style onto the functional landscape, where we can think of test-driven design as building a web of interrelated facts.