A few minutes into my first real TDD trip in Clojure I discovered there is two reasonable ways I can do tests: Mocking or black-box testing. I decided to go for mocking, and so I discovered clojure.contrib.mock
. I found the docs fairly confusing, but finally understood it with a little help of this article and research.
Verify Calls to a Function
Assuming we have a function to compute square of a number, we want to write another function for square of sum.
(defn square [x] (* x x))
Our test can look like this:
(ns squirrel.test.core (:use [[clojure.test]]) (:use [[clojure.contrib.mock]])) (deftest test-square-of-sum (expect [square (has-args [3])] (square-of-sum 2 1)))
This use of expect
asserts that when we execute the inner form (square-of-sum 2 1)
, it calls square
with argument equal 3. However, it does not execute square
itself. The only thing that this test checks is whether square
got called. In particular, it does not check what (square-of-sum 2 1)
returns. We’ll get back to stubbing in a moment.
Stubbing
Let’s modify our test to also assert the final result:
(deftest test-square-of-sum (expect [square (has-args [3])] (is (= 9 (square-of-sum 2 1))))
When the test runs, it fails because square-of-sum returns nil
. The reason is that expect
replaces square
with a stub which by default doesn’t return anything.
To have stub return a concrete value, we can use the returns
function:
(deftest test-square-of-sum (expect [square (returns 9 (has-args [3]))] (is (= 9 (square-of-sum 2 1))))
Voila. Now the test passes.
To recap, what this two-line test does is:
- Create a stub for
square
which returns 9 for argument of 3. - Assert this stub is called with argument 3.
- Assert that
square-of-sum
calls this stub with argument 3. - Assert that
square-of-sum
eventually returns the correct value.
Quite a lot for such a tiny test.
Expectation Hash
You may be wondering what exactly the second argument in each binding pair is. Clojure docs call it expectation hash.
Each function that operates on an expectation hash, such as has-args
or returns
, has two overloaded versions. One of them only takes a value or predicate and returns a new expectation hash. Examples include (returns 9)
or (has-args [3])
.
The other version takes an expectation hash as the second argument. These versions are used to pass the expectation hash through a chain of decorators. Order of decoration does not matter, so (returns 9 (has-args [3]))
is effectively the same as (has-args [3] (returns 9))
.
I started out using clojure.contrib.mock, but my needs outstripped it, so I wrote my own package, Midje. It’s grown to be a lot more than just a mocking library.
https://github.com/marick/Midje
Hey, thanks for the simple and straightforward intro to stubbing in clojure. It was very helpful.