I continue to use little experiments to help me think through TDD in Clojure. (I plan to begin a realistic experiment soon.) Right now, I’m mainly focused on three questions:
What would mocking or stubbing mean in a strict(ish) functional language?
What’d be a good mocking notation for Clojure?
How do you balance the outside-in style associated with mocks and the bottom-up style that the REPL (interpreter) encourages?
Here’s an example from Conway’s Game of Life. It begins with an implementation suggestion from Paul Blair and Michael Nicholaides at the Philly Code Retreat. Instead of thinking of the board as a 2×2 array of cells, with some of them dead and some alive, think instead only of living cells, each of which knows its coordinates. Here’s an example that shows how “blinkers” blink from generation to generation.
A couple of things have happened here:
This is my notation for a straightforward non-stubbing test. The value on the left is executed and it’s compared (for equality) to the value on the right.
I’ve started coding outside-in, and I’ve named the first function I need:
The Blair/Nicholaides approach advances the “world” to the next generation by (conceptually) adding dead cells around the edge of all the living cells, running the normal life rules that govern how cells change because of their neighbors, and then throwing away all the cells that end up dead. In other words:
pendingbit is just there because (sadly) Clojure makes you declare functions before mentioning them.
pendingjust creates functions that print that they’ve not yet been implemented.
The rest of the code flows the
worldargument through a pipeline of three functions. If you’re not familiar with the
->macro, the result is the same as this:
I don’t feel the need to test this code now because it’s really declarative—it says what it means to produce a next world under this approach. (It will be tested in the very end by the “integration test” that shows a blinker working.)
I can now implement any of the three new functions. I’ll pick
tick because it seems to be the heart of the matter. Here’s a first implementation:
There are two odd things going on here.
I think of the Clojure programs I’ve written this way: there’s a big stateful universe out there. At some moment, a snapshot of relevant bits of that universe is presented to my pure functional program. It churns away, presents the results back to the big stateful universe, and then disappears. Given that bias, I find it convenient to think of the universe-bits not as data to be passed around, but as the unchanging background behind my code’s calculations. Any of that code should be able to reach out and grab bits of the relevant background by calling dynamically bound functions of no arguments.
against-background, any code operating inside
successor(or inside functions it calls) can call
(world)and magically get the original
bordered-world. As you’ll see, this lets me put off committing to a representation for cells. (The representation I used for the blinkers isn’t enough after the border’s been added, since there’s now a need to distinguish between living and dead cells.)
This essay is supposed to be a sketch of TDD in Clojure, but here’s the second function I’ve written without tests. What’s up with that? I’ve tentatively concluded that testing functions that map one function over a sequence is the moral equivalent of testing getters and setters: not worth the trouble. But don’t fear,
successoris worth testing, and we’ll do that next. Almost next.
First, stubbing function calls.
In object-oriented languages, I think of mock-driven-design as a way of teasing out collaborators for the object I’m building. I push responsibilities for work onto objects that I’ll implement later. Mocking lets me defer the implementation of those objects until I’m ready, and creating some examples of the API teaches me the (implicit) specification for the new object.
I’ve found that with pure functional programs that don’t modify state, it makes more sense to think of a function like
(f 2) => 4 as a fact. What I’m doing as I test-drive a function is describing how facts about its inputs and outputs depend on other facts, in an almost Prolog-like way. For example, consider this code:
That says that, for any cell you care to provide,
f of that cell will be 10, provided
g of that cell is true and
h is 2. If either of those latter two facts don’t apply to the cell, I’m not saying what
f’s value is.
I use the funny
...cell... notation in the way that mathematicians use n to talk about any integer. (They call that universal quantification.) I don’t want to create a particular cell because I might need to specify properties that have nothing to do with the function I’m working on. This notation says that nothing about the cell is relevant except for what comes after the
Here’s one way to write a Life rule in this notation:
falsey bit in the first line is because Clojure has two distinct values that can mean “false”.
falsey is a function that takes the result of the left-hand side and fails the test if that result is anything other than one of the two false values. I’m using it because I don’t want to overspecify
living?. There’s no reason to care which of the two “false” values it returns.
There’s a problem with this test, though. Remember what I said above: the left-hand side gets evaluated and handed to
falsey. That means
living? has to have a definition—which means I’d have to settle on how the code knows whether a cell is alive or dead. I like doing one thing at a time and putting off decisions as long as I can, and right now I’d rather be focused on
successor instead of cell representations.
Here’s a way to defer that decision:
Here I’m saying something subtly different than before. I’m saying that the result of
successor is specifically that cell produced by calling
killed on the original cell. The
=means=> notation tells the framework to create a mock instead of evaluating the right-hand side for its value. In a more familiar mocking syntax (for Ruby), the whole test is equivalent to:
OK. The next figure gives the whole set of Life rules, expressed as executable tests. (Well, executable as soon as I implement the testing framework.) Notice that I called the outer wrapper
know (a fact) instead of
know seems more appropriate for rules. The two forms mean the same thing.
Notice also that I implemented a notation for saying “run this test for each value in a sequence”. The use of commas, as in
[4,,,8], indicates that—conceptually—the fact is true for all values four through eight. Only the ones listed are actually tried. (Commas count as >white space in Clojure.)
This isn’t the tersest possible format—a table would be better—but it’ll do. I think it’s reasonably readable. Do you?
Here, for reference, is code that passes the test:
We now have an expanded choice of functions to write:
I could go breadth-first—with
unborder—or go depth-first with one of the functions on the second line. In this particular case, I’d rather go depth first. I’ve avoided deciding on a representation, so I don’t know yet what
border should do.
If this installment meets your approval, I’ll add another one that begins work on—oh—probably
living-neighbor-count is the most complicated, so it’s a good one to chip away at.