TDD in Clojure: Mocking & Stubbing

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)).

2 thoughts on “TDD in Clojure: Mocking & Stubbing

Leave a Reply

Your email address will not be published. Required fields are marked *

Spam protection by WP Captcha-Free