Skip to content
marick edited this page Apr 27, 2012 · 5 revisions

One Clojure idiom for reaching functions that have been declared private is to call them via their var, rather than via an interned symbol:

    (scratch.core/super-secret-function 5)   ; doesn't work
    (#'scratch.core/super-secret-function 5) ; works

That works fine inside facts:

    (fact
      (#'scratch.core/super-secret-function 1) => 6)

You can also use vars in prerequisites:

    (fact
      (counter 4) => 8
      (provided
        (#'scratch.core/super-secret-function 4) => 4))

testable-privates

Calling private functions through vars looks ugly, so there's a utility function to make them available in the test namespace:

(use '[midje.util :only [testable-privates]])

(testable-privates scratch.core super-secret-function)

(fact (super-secret-function 4) => 4)

Notice that testable-privates isn't part of Midje's default interface.

Such testable privates can't be used in prerequisites. testable-privates creates a new var, so in the above program, there are two vars pointing at the super-secret function:

#'scratch.core/super-secret-function
#'scratch.core-tests/super-secret-function   ;; Created by testable-privates

Prerequisites work by temporarily pointing vars at new functions. Therefore, in this code:

    (fact
      (counter 4) => 8
      (provided
        (super-secret-function 4) => 4))

... the prerequisite overrides the test namespace's var (#'scratch.core-tests/super-secret-function) while leaving the original var pointing to the original function. Since the code for counter is in the source namespace, its call to super-secret-function uses the un-overridden var (#'scratch.core/super-secret-function).

expose-testables

Consider that tests---while being clients of the code under test---are a special kind of client, one that can more reasonably be expected to have some (if not total) knowledge of a namespace's internals. We can formalize that distinction by declaring that an otherwise-private function should be available to tests. That's done with metadata:

   (defn- ^{:testable true } 
      mockable-funcall? [x] ...)

Then, within the test namespace, all such "testables" can be interned:

Clone this wiki locally