Question

Is there any way to mock (not stub) a protocol function with Midje (clojure) using something like the "provided" syntax?

This is simial to the question in: Mocking Clojure protocols, but with mocking.

In more detail: I have a protocol and a function that returns something that implements it. I would like to stub the function to return a mock of the protocol and I would like to register an expectation on one of the functions of the mocked protocol "implementation".

edit - here is an example:

There is a protocol and it's implementation:

(defprotocol Thiny (go-bump [_ _]))
(deftype TheThing []
  Thiny
  (go-bump [_ _] 23))

There is a function that returns an implementation of a protocol:

(defn gimme [] (TheThing.))

TheThing might be a DB or network connection or some other nasty thing you want to get rid of in the test.

Then, there is the function I want to test:

(defn test-me [n]
  (let [t (gimme)]
    (-> t (go-bump n))))

I want to make sure it calls go-bump with n.

This is my first attempt to create a test. But it is only halfway done, I would like to setup expectations on the Thiny returned by gimme:

(fact
  (test-me 42) => 42
  (provided (gimme) => (reify Thiny (go-bump [_ n] n))))
Was it helpful?

Solution 2

For posterity. Here is a working test:

(fact
  (test-me 42) => 42
  (provided (gimme) => :the-thingy)
  (provided (go-bump :the-thingy 42) => 42))

The trick was to use multiple provided statements that tie into each other.

Wierd observation. The same way of testing doesn't work for a function that uses the other syntax for calling functions on the protocol. No idea why.

(defn test-me2 [n]
  (let [t (gimme)]
     (.go-bump t n)))

OTHER TIPS

Mocking protocols should be no different than mocking functions, you need to consider that the first dispaching parameter is this and so the mocking function should take that type in consideration.

For instance, given a protocol P:

(defprotocol P 
  (foo [this]) 
  (bar-me [this] [this y]))

Extended for type Integer

(extend-protocol P
  Integer
  (foo [this]
    (+ this 4))
  (bar-me [this]
    (* this 5))
  (bar-me [this y]
    (+ this y)))

You can check a couple things first, there's no implementation for Long:

(foo 3)
=>  IllegalArgumentException No implementation of method: :foo of 
    protocol: #'P found for class: java.lang.Long  
    clojure.core/-cache-protocol-fn (core_deftype.clj:541)

Works as expected for ints:

(foo (int 3))
=> 7

Now define the fact and provide the protocol function as you need:

(fact
  (foo (int 3)) => 7
  (provided (foo 3) => 8))

In this case it properly fails since the mock is returning 8 instead of 7 for the specified input.

FAIL at (test.clj:20)
     Expected: 7
     Actual: 8

If value mocks are not enough and you need to provide an alternative implementation instead, take a look at with-redefs-fn, you can wrap tested functions with it.

 => (defn f [] false)
 => (println (f))
 ;; false
 => (with-redefs-fn {#'f (fn [] true)}
 #(println (f)))
 ;; true

There's also a discussion in GitHub about his with a couple alternatives for runtime dispatching mocks.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top