I’m really having a good time building tic-tac-toe in Clojure, but there are definitely still a few snags with learning a new language- especially a functional one. One of these has been properly mocking out functions for tests.

Fortunately, Clojure has the dangerously powerful with-redefs function, which both Ginny and Eric have written about. I say dangerously powerful because it’s very simple to redefine any function you want. That makes it easy to have a test passing that might not even really test anything (which I suppose is true with many mocks and stubs).

I was able to use with-redefs to mock out a single read-line call that gets input from a user and tests a few of my console UI functions. Here’s a simple example of getting a move.

(it "should get a move from a human-player"
    (with-redefs [read-line (constantly "1")]
        (should= 1 (get-human-move))))

However, I was hitting a wall when trying to put together a series of inputs that could test behavior for invalid input or even an entire game loop.

If I learned one thing during my last foray into tic-tac-toe it’s that there is “the struggle” that’s somewhat necessary to help things sink in, and then there’s the useless banging of the head against a wall. I spent a couple of days thinking about how to simulate the input with no real solutions, so before I spent anytime engaging in the latter activity I decided to take a peek at Colin’s test file for his tic-tac-toe implementation in Clojure, where I discovered not one, but two, gems.

First up is the with-in-str function. It’s part of the Clojure core but I hadn’t checked it out yet. This function does a very similar thing to what I was doing with redefining read-line, but as soon as I saw it knew that this is where I would pass a sequence of inputs. And that’s when I saw the second gem: Colin created a function called make-input that created a lazy sequence of inputs that could be passed to with-in-str.

(defn make-input [coll]
  (apply str (interleave coll (repat "\n"))))

The interleave function is the key here, as it creates the lazy sequence, in this case placing a “\n” between each item in the collection that you pass it. So, if I want to test that my get-human-move rejects words and gets only valid numbers I can do this:

(it "shoud reject a non number and get another input"
        (with-in-str (make-input '("nine" 3))
            (should= 3 (get-human-move))))

Lazy sequences are definitely something I’m working on wrapping my brain around so the discovery of Colin’s make-input method was a sweet two-for.

(Thanks as well to Ginny for tipping me off to the ClojureDocs Clojure Core page. For the past month I’ve had at least one browser tab always open to it.)