Monkeying with Clojure’s deftest
Say you have a namespace a
that needs to be tested: 1
(ns a)
(defn ^{:private true} foo [] 42)
Using Clojure’s clojure.test
libs you might think it would be as simple as the following:
(ns b
(:use [clojure.test :only [deftest is]]))
(deftest test-foo
(is (= 42 (a/foo))))
; java.lang.IllegalStateException: var: #'a/foo is not public
Whoops! That function a/foo
is private and not readily available to call. Now there are a number of ways to get at that function to test it, but the shortest path is the following:
(deftest test-foo
(is (= 42 (#'a/foo))))
(test-ns 'b)
; Testing b
;=> {:test 1, :pass 1, :fail 0, :error 0}
So using the #'
reader macro we can resolve the a/foo
var directly, but what if there was a way to declare a test that specifically for testing private vars:
(defmacro defprivatetest
[name [local-name private-name] & body]
(when *load-tests*
`(def ~(vary-meta name
assoc
:test `(let [~local-name (resolve '~private-name)]
(fn [] ~@body)))
(fn [] (test-var (var ~name))))))
This is almost exactly like the implementation of clojure.test/deftest
except that it allows for a single named binding that is resolved to a function (private or not) in another namespace, like so:
(defprivatetest test-a
[foo a/foo]
(is (= 42 (foo))))
(test-ns 'b)
;=> {:test 1, :pass 1, :fail 0, :error 0}
Granted this is a lot of work just to avoid #'
, but this is much more flexible in the face of change.
I leave it to the reader to change the implementation to allow multiple bindings.
:f
-
This post is much ado about nothing, but I thought I’d push it into the ether while everyone else is distracted by Clojure Conj. ↩
3 Comments, Comment or Ping
icemaze
Why don’t you just test in the same namespace? If you worry about namespace pollution you can just write your tests in a different file that doesn’t get loaded in production. Is there some hidden benefit I’m missing?
Sep 3rd, 2010
fogus
I do not like to to keep the tests with the code under test. This is my psychosis. The part that I think is not clarified is that when testing the namespace private Vars (in this case functions) are not immediately accessible from another namespace (the test ns for example). I like to change my code alot, but hate to go and change my tests, so I’m looking for a way to (effectively) be lazy here.
Thanks for posting.
:f
Sep 3rd, 2010
cgrand
why not simply implement a refer-privates or refer-all fn? (.refer ns ‘x #’foo/x) works like a charm even when x is private.
Sep 30th, 2010
Reply to “Monkeying with Clojure’s deftest”