Home Contact GitHub
Stubbing in Clojure with Speclj

Speclj is a great testing framework for Clojure and ClojureScript, but I’ve found there are few easy examples around demonstrating how to stub and spy using Speclj.

The situation is that we want to inject one function with another and assert that the top function calls the injected function. This is one way to drive the development of a top function and ensure full test coverage.

So let’s see some basic examples of how to stub/spy with Speclj in Clojure. First, I’ve just defined a function add that adds two numbers together, which we’re going to stub and test.

(defn add [a b]
  (+ a b))

Here’s how we can write a test asserting that add was called:

(describe "example name"
          (with-stubs)

          (it "add was called"
              (with-redefs [add (stub :add)]
                (should-have-invoked :add))))

In order to stub a function we must first call (with-stubs). We can then create a stub named :add with the call (stub :add). NB: if you want to stub out a return value, you can do it like so: (stub :foo {:return :bar}).

We can use with-redefs to test interactions between functions: we basically redirect the name of the function to point to our stub function instead of to the real function. In this case, we redefine add to point to our :add stub.

should-have-invoked is then used to assert that the stub should have been called.

Now let’s see how to assert that a stub was called with a specific argument:

(should-have-invoked :add {:with [1 2]}))))

This asserts that the function :add was called with the parameters 1 and 2.

We can also assert about the number of times that a stub function was called:

(should-have-invoked :add {:with [1 2] :times 2}))))

Gotcha

Be careful when using :with to assert on the called parameters: you must put the arguments in [] even if there’s only a single argument, as otherwise you’d never be able to pass an array as an expected argument. So if you expect your function to be called with an array you would use something like:

(should-have-invoked :max {:with [[1, 2, 3]]})