Siva and I were pairing on a unit-test that involved writing something to HBase. When Siva said that mocking the call to the save-to-hbase function would make testing easier (a simple thing using JMock, he said), I decided to write a quick mocking utility for Clojure.
Then later, we realized that we wanted to go one step further. The row-id that was used as the key to the object in HBase was generated using system-time. That meant that even if we wanted to confirm that the object was indeed saved, we had no way of knowing what the row-id was. One solution to such a problem is to inject the row-id in (instead of being tightly coupled to the function that generated the row-id). Instead, I wrote a stubbing utility that makes this arbitrarily easy to do.
So here they are – mocking and stubbing – packaged up as the conjure project on github.
The set up
Imagine we had the following functions –
(defn xx [a b] 10) (defn yy [z] 20) (defn fn-under-test [] (xx 1 2) (yy "blah")) (defn another-fn-under-test [] (+ (xx nil nil) (yy nil)))
Also imagine that we had to test fn-under-test and another-fn-under-test, and we didn’t want to have to deal with the xx or yy functions. Maybe they’re horrible functions that open connections to computers running Windoze or something, I dunno.
Mocking
Here’s how we might mock them out –
(deftest test-basic-mocking (mocking [xx yy] (fn-under-test)) (verify-call-times-for xx 1) (verify-call-times-for yy 1) (verify-first-call-args-for xx 1 2) (verify-first-call-args-for yy "blah"))
Pretty straightforward, eh? You just use the mocking macro, specifying all the functions that need to be mocked out. Then, within the scope of mocking, you call your functions that need to be tested. The calls to the specified functions will get mocked out (they won’t occur), and you can then use things like verify-call-times-for and verify-first-call-args-for to ensure things worked as expected.
Stubbing
As mentioned in the intro to this post, sometimes your tests need to specify values to be returned by the functions being mocked out. That’s where stubbing comes in. Here’s how it works –
(deftest test-basic-stubbing (is (= (another-fn-under-test) 30)) (stubbing [xx 1 yy 2] (is (= (another-fn-under-test) 3))))
So that’s it! Pretty simple. Note how within the scope of stubbing, xx returns 1 and yy returns 2. Now, for the implementation.
Implementation
The code is almost embarrassingly straight-forward. Take a look –
(ns org.rathore.amit.conjure.core (:use clojure.test)) (def call-times (atom {})) (defn stub-fn [function-name return-value] (swap! call-times assoc function-name []) (fn [& args] (swap! call-times update-in [function-name] conj args) return-value)) (defn mock-fn [function-name] (stub-fn function-name nil)) (defn verify-call-times-for [fn-name number] (is (= number (count (@call-times fn-name))))) (defn verify-first-call-args-for [fn-name & args] (is (= args (first (@call-times fn-name))))) (defn verify-nth-call-args-for [n fn-name & args] (is (= args (nth (@call-times fn-name) (dec n))))) (defn clear-calls [] (reset! call-times {})) (defmacro mocking [fn-names & body] (let [mocks (map #(list 'mock-fn %) fn-names)] `(binding [~@(interleave fn-names mocks)] ~@body))) (defmacro stubbing [stub-forms & body] (let [stub-pairs (partition 2 stub-forms) fn-names (map first stub-pairs) stubs (map #(list 'stub-fn (first %) (last %)) stub-pairs)] `(binding [~@(interleave fn-names stubs)] ~@body)))
It’s just an hour or so of work, so it’s probably rough, and certainly doesn’t support more complex features of other mocking/stubbing libraries. But I thought the simplicity was enjoyable.
Hello
link to project on github is wrong – it points to your site….
FYI, there’s already a clojure project named conjure: http://github.com/macourtney/Conjure
Raph
Do you have a good solution for clojure <1.3. The binding version doesn't work if the dynamic method resoltion is not activated.
You can activate the behaviour like this:
(defn ^:dynamic function-to-be-stubbed-out [])
in clojure 1.3 you can use with-redefs http://clojuredocs.org/clojure_core/clojure.core/with-redefs. Unlike using binding to change a dynamic var which is visible only to the current thread, with-redefs changes the root binding so the change is visible to all threads… but for testing this shouldn’t be problematic.
Thanks for the interesting post. I am plnyaig around with Clojure and RDF technologies as well. I am invoking the OpenRDF Sesame Java API via Clojure. Like you I do not know exactly where I am going but my intuition tells me something interesting may come out of this exploration. For one, you mention Prote9ge9. It seems like you may be able to bypass Prote9ge9 and have a command-line or shell (or even DSL) to directly create/read/update ontologies/RDF directly from the Clojure REPL. Also if you load various triple stores, and you are good enough at Clojure, you can start slicing and dicing RDF interesting and perhaps novel ways from the REPL. Good luck, and let us know how all this is going.
Hi guys,
I’ve updated Conjure to work with Clojure 1.3, and I’ve also tested it with Clojure 1.4.0-beta3.
Let me know!
– Amit.
naturally like your web site but you need to check the spelling on quite
a few of your posts. A number of them are rife with spelling problems and I
to find it very troublesome to inform the reality nevertheless
I’ll certainly come back again.
Hi there to all, because I am genuinely eager of reading this website’s post to be updated
daily. It consists of nice material.