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 )] (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.
Let’s modify our test to also assert the final result:
(deftest test-square-of-sum (expect [square (has-args )] (is (= 9 (square-of-sum 2 1))))
When the test runs, it fails because square-of-sum returns
nil. The reason is that
square with a stub which by default doesn’t return anything.
To have stub return a concrete value, we can use the
(deftest test-square-of-sum (expect [square (returns 9 (has-args ))] (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
squarewhich returns 9 for argument of 3.
- Assert this stub is called with argument 3.
- Assert that
square-of-sumcalls this stub with argument 3.
- Assert that
square-of-sumeventually returns the correct value.
Quite a lot for such a tiny test.
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
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
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 )) is effectively the same as
(has-args  (returns 9)).