how to mock a specific implementation of a protocol? - clojure

How to mock a specific implementation of a protocol, preferably with plain Clojure i.e. no external libs e.g. Midje? I know how to mock all implementations but not specific ones:
(defprotocol P
(foo [p]))
(defrecord R1 [])
(defrecord R2 [])
(extend-protocol P
R1
(foo [_] :r1)
R2
(foo [_] :r2))
(foo (->R1)) ;; :r1
(foo (->R2)) ;; :r2
;; now the mocking ...
(with-redefs [foo (constantly :redefed)]
(println (foo (->R1))) ;; :redefed
(println (foo (->R2)))) ;; :redefed
i.e. how to get (foo (->R1)) to return :redefed while (foo (->R2)) still returns :r2?
Assume I can only make changes below the now the mocking ... comment. Notice that I am putting aside the recommendation to extend a protocol only if I have control of the protocol, the type, or both.

You could create a new protocol containing methods with the same signature and use that when rebinding:
(def foo-orig foo)
(defprotocol Q
(bar [q]))
(extend-protocol Q
R1
(bar [_] :redefed)
R2
(bar [this] (foo-orig this)))
Note you'll have to capture the original foo definition for the implementations you don't want to change. Then:
(with-redefs [foo bar]
(println (foo (->R1))) ;; :redefed
(println (foo (->R2)))) ;; :r2
or you could defined a multimethod e.g.
(defmulti bar (fn [q] (type q)))
(defmethod bar R1 [_]
:redefed)
(defmethod bar :default [q]
(foo-orig q))

My first thought is to have the foo impls delegate to a helper fn:
(extend-protocol P
R1
(foo [p] (foo-r1 p))
R2
(foo [p] (foo-r2 p)))
with
(defn foo-r1 [_] :r1)
(defn foo-r2 [_] :r2)
and then just redef foo-r1 and foo-r2 independently as desired.
Note also that with-redefs is meant to operate on var instances, while the foo you define as part of a protocol is not the same as a var. That may be the cause of your global redefinition problem.
Update
You may need to clarify your use-case & update the question. See this part at clojure.org:
Extend only things you control You should extend a protocol to a type
only if you control the type, the protocol, or both. This is
particularly important for the protocols included with Clojure itself.

It might be easiest to define wrapper functions for protocol internal implementation functions and call them from outside.
Then it can be mocked easily with with-redefs et al.
It also has other benefits, e.g. being able to define specs for such functions.

Don't pass foo an R1 at all. Define a new implementation, R1Mocked, that does whatever you want, and pass that instead. This is exactly the sort of polymorphism that protocols are designed to support.

Related

Fuse type/fn pairs into polymorphic fn via 'anonymous' protocol & method

My goal is a function/macro that works like this:
(def f (polymorphic-fn
java.lang.Long (fn [a] (inc a))
java.lang.String (fn [a] (str a "x"))))
(f 1) ;; => 2
(f "abc") ;; => "abcx"
Since the type-based dispatch of protocols has the best performance I was thinking to create an 'anonymous' protocol for the 'fused' function with a macro like this:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym)
method (gensym)
extends (for [[type handler] pairs]
`(extend ~type ~proto {(keyword ~method) ~handler}))]
`(do
(defprotocol ~proto (~method [e#]))
~#extends
~method)))
This produces the error: Unable to resolve symbol: G__18707.
Is there a way to return the 'anonymous' method, or is there a better way to implement such a function?
The problem is that the defprotocol will generate code that will intern the protocol methods. Ie. after macroexpansion, the symbols for your defined method is still not known to the compiler. Thus, the compilation fails and will barf that the symbol is unknown.
Many other def...'s will generate a macro "call" that will intern the symbol during macro expansion (and thus the compiler will remain happy).
To fix it you can just declare it beforehand. This works since declare is macro, will get expanded and the compiler will be happy:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym "proto")
method (gensym "prot-method-")
extends (for [[type handler] (partition 2 pairs)]
`(extend ~type ~proto {~(keyword (str method)) ~handler}))]
`(do
(declare ~method)
(defprotocol ~proto (~method [e#]))
~#extends
~method)))
Note: I also fixed the keyword call in this.
I think you just want to use regular protocols, along with extend-type:
(defprotocol Fooable
(foo [this]) )
(extend-type java.lang.Long
Fooable
(foo [some-long] (inc some-long)))
(extend-type java.lang.String
Fooable
(foo [any-string] (str any-string "-and-more")))
with result:
(foo 3) => 4
(foo "hello") => "hello-and-more"
It may be possible to use a macro to hide the protocol name by using an auto-gensym, but I don't see the point. Just ignore the protocol name 'Fooable' and you have the same result.
Also, be aware that parts of Clojure implementation create concrete Java classes behind the scenes, which may require a hard-coded name.
You could also mimic the protocol functionality by using a cond:
(defn bar [it]
(cond
(= (class it) java.lang.Long) (inc it)
(= (class it) java.lang.String) (str it "-and-more")))
(bar 7) => 8
(bar "farewell") => "farewell-and-more"
You could define a function to generate bar like you do with polymorphic-fn if you wanted.

dependency injection between app and library in clojure

I am splitting part of my app into a library
The library functionality has certain dependencies which must be injected by the app. I have modeled this with a protocol
(defprotocol MyLibDependencies
(some-injected-capability [x]))
(defrecord Foo [y])
(defrecord Bar [y])
(defmulti render-response class)
(defmethod render-response Foo [val] {:ok (some-injected-capability (:y val))})
(defmethod render-response Bar [val] {:err (some-injected-capability (:y val))})
and here in the application I can provide an implementation:
(extend-type Object
MyLibDependencies
(some-injected-capability [x] (inc x)))
(comment
(render-response (Foo. 10)) ;; => {:ok 11}
(render-response (Bar. 10)) ;; => {:err 11}
)
This works, however it feels like an abuse of protocols, because I need neither polymorphic dispatch, nor does the injected function necessarily require an argument (the protocol requires at least one argument to dispatch against it's class). What are my options?
Note that the records Foo and Bar are library domain types, and the render-response method is also library domain. I don't necessarily care how I define them, but the abstractions they represent are library domain.
This is a bit closer to how you would typically see protocols used to provide functionality from client code to a library:
;; lib.clj
(defprotocol MyLibDependencies
(provide-status [this x])
(provide-response [this x]))
(defn render-response
[responder val]
{:status (provide-status responder val)
:code (provide-response responder val)})
;; client.clj
(defrecord Foo [y]
MyLibDependencies
(provide-status [this val]
(if (even? val)
:ok
:err))
(provide-response [this val]
(+ y val)))
(defrecord Bar [y]
MyLibDependencies
(provide-status [this val]
(if (odd? val)
:ok
:err))
(provide-response [this val]
(+ y val)))
(comment
(render-response (Bar. 10) 1) ;; => {:status :ok :code 11}
(render-response (Foo. 10) 1) ;; => {:status :err :code 11}
)
There are numerous examples of this style of Clojure code in the wild - in fact most of the core functions that make up Clojure itself are end up resolving to calls to protocol methods provided by the specific datastructure being used, or multimethod calls extended for the individual datatypes.

Mocking protocol implementations in Midje

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

Using a protocol with primitive arguments

I'm trying to define a protocol in Clojure 1.4 with primitive arguments (so that I can avoid unnecessary primitive boxing in performance-sensitive code):
(defprotocol A
(foo [a ^long x]))
(extend-type java.lang.String A
(foo [s ^long x] (.charAt s x)))
This look like it works OK but fails with an exception when I try to use it:
(foo "abracadarbra" 3)
=> ClassCastException XXXX cannot be cast to clojure.lang.IFn$OLO
What am I doing wrong?
After some further reasearch it appears that protocols do not yet support primitive type hints (as of Clojure 1.4). See e.g. https://groups.google.com/d/topic/clojure-dev/HxBqIewc494/discussion
Alternatives seem to be:
Write regular functions with primitive hints. You lose polymorphism.
Use a Java interface (you can use reify to build instances in Clojure)
take the type hint out of the defprotocol and leave it in the extend-type
(defprotocol A
(foo [a x]))
(extend-type java.lang.String A
(foo [s ^Long x] (.charAt s x)))
nil
core> (foo "abracadarbra" 3)
\a
or you can change the type hint like so:
(defprotocol A
(foo [a ^Long/TYPE x]))
(extend-type java.lang.String A
(foo [s ^long x] (.charAt s x)))
nil
core> (foo "abracadarbra" 3)
\a
this still produces no reflection warnings without the hint in the defprotocol
EDIT:
(extend-type java.lang.String A
(foo [s ^Long x] (type x)))
nil
core> (foo "abracadarbra" 3)
java.lang.Long

Clojure namespace: method to see defined objects?

Is there some way to see what has already been defined in a clojure session (equivalent to calling ls())? Let's say that I create a few objects:
(def x 1)
(def y 2.2)
(def plus-one (fn [x] (+ x 1)))
Is there a command that can be run to show me that these now exist in the user namespace?
I am doing all the assignments in user namespace.
user> (def *foo 10)
#'user/*foo
;; To see all the public intern mappings in the user namespace.
user> (ns-publics 'user)
{*foo #'user/*foo}
Now let's define a function which is not public
user> (defn- foobar[x]
(println x)
#'user/foobar
When you call ns-publics function. It will not show foobar function in the mappings.
user> (ns-publics 'user)
{*foo #'user/*foo}
To see the intern mappings for the namespace. Use (ns-interns 'YOUR-NAMESPACE)
user> (ns-interns 'user)
{foobar #'user/foobar, *foo #'user/*foo}
Maybe ns-publics ?
Returns a map of the public intern mappings for the namespace.
or ns-map ?
Returns a map of all the mappings for the namespace.
As I understand it, there is no "global environment," there are only namespaces. Of course whichever one you are currently "in" looks like a "global environment" for practical purposes.