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)))
Related
Given that :post takes a form that gets evaluated later (e.g. {:post [(= 10 %)]}). How could one dynamically pass a 'pre-made' vector of functions to :post?
For example:
(def my-post-validator
[prediate1 predicate2 predicate3])
(defn foo [x]
{:post my-post-validator}
x)
this throws a syntax error
Don't know how to create ISeq from: clojure.lang.Symbol
With my fuzzy understanding, it's because defn is a macro, and the thing that allows the % syntax in :post is that it's quoted internally..?
I thought maybe I then use a macro to pass a 'literal' of what I wanted evaluated
(defmacro my-post-cond [spec]
'[(assert spec %) (predicate2 %) (predicate n)])
example:
(defn foo [x]
{:post (my-post-cond :what/ever)}
x)
However, this attempt gives the error:
Can't take value of a macro
Is there a way to pass a vector of things to :post rather than having to define it inline?
You can't pass a vector of predefined predicates, but you can combine multiple predicates under a single name and use that name in :post:
(defn my-post-cond [spec val]
(and
;; Not sure if this is exactly what you want,
;; given that `val` becomes an assert message.
(assert spec val)
(predicate2 val)
;; You used `n` - I assume it was supposed to be `%`.
(predicate val)))
(defn foo [x]
{:post [(my-post-cond :what/ever %)]}
x)
I started off as a fan of pre- and post-conditions, but I've changed over the years.
For simple things, I prefer to use Plumatic Schema to not only test inputs & outputs, but to document them as well.
For more complicated tests & verifications, I just put in an explicit assert or similar. I also wrote a helper function in the Tupelo library to reduce repetition, etc when debugging or verifying return values:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn oddly
"Transforms its input. Throws if result is not odd"
[x]
(let [answer (-> x (* 3) (+ 2))]
(with-result answer
(newline)
(println :given x)
(assert (odd? answer))
(println :returning answer))))
(dotest
(is= 5 (oddly 1))
(throws? (oddly 2)))
with result
------------------------------------
Clojure 1.10.3 Java 11.0.11
------------------------------------
Testing tst.demo.core
:given 1
:returning 5
:given 2
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
Passed all tests
So with either the println or assert, the returned value is easy to see. If it fails the assert, an Exception is thrown as normal.
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.
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.
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.
I write a game server and have to check that messages arriving from users are correct and valid. That means they have to be of correct syntax, comply to parameter formatting and are semantically correct, i.e. complying to the game's rules.
My goal is to have an expressive, functional way without throwing exceptions that allows composability as good as possible.
I am aware of other similar questions but they either refer to {:pre ..., :post ...} which I dislike as only stringified information can be processed once the exception is thrown, or refer exception handling in general which I dislike because Clojure should be able to do this kind of task, or they refer to Haskell's monadic style with e.g. a maybe Monad à la (-> [err succ]) which I also dislike because Clojure should be able to handle this kind of task without needing a Monad.
So far I do the ugly way using cond as pre-condition checker and error codes which I then send back to the client who send the request:
(defn msg-handler [client {:keys [version step game args] :as msg}]
(cond
(nil? msg) :4001
(not (valid-message? msg)) :4002
(not (valid-version? version)) :5050
(not (valid-step? step)) :4003
(not (valid-game-id? game)) :4004
(not (valid-args? args)) :4007
:else (step-handler client step game args)))
and similar...
(defn start-game [game-id client]
(let [games #*games*
game (get games game-id)
state (:state game)
players (:players game)]
(cond
(< (count players) 2) :4120
(= state :started) :4093
(= state :finished) :4100
:else ...)))
Another way would be to write a macro, similar to defn and {:pre} but instead of throwing an AssertionError throw an ex-info with a map, but again: opposed to exception throwing.
The heart of your question seems to be in your comment:
Yeah, maybe I should explain what about this is "ugly": It's the not-well composability of just return an actual result, which can be represented almost arbitrarily and a numbered keyword as error. My callstack looks like this: (-> msg msg-handler step-handler step-N sub-function), and all of them could return this one error type, but none of them returns the same success type and another thing is, that I have to manually design if en error return type should short-circuit or has to be expected to do some intermediate work (undo data changes or notify other clients in parallel)
Clojure 1.5+ has the some-> threading macro to get rid of nil? check boilerplate. We just need to gently adjust the code to substitute the nil? check for a check of our choice.
(defmacro pred->
"When predicate is not satisfied, threads expression into the first form
(via ->), and when that result does not satisfy the predicate, through the
next, etc. If an expression satisfies the predicate, that expression is
returned without evaluating additional forms."
[expr pred & forms]
(let [g (gensym)
pstep (fn [step] `(if (~pred ~g) ~g (-> ~g ~step)))]
`(let [~g ~expr
~#(interleave (repeat g) (map pstep forms))]
~g)))
Note with this definition, (pred-> expr nil? form1 form2 ...) is (some-> expr form1 form2...). But now we can use other predicates.
Example
(defn foo [x] (if (even? x) :error-even (inc x)))
(defn bar [x] (if (zero? (mod x 3)) :error-multiple-of-three (inc x)))
(pred-> 1 keyword? foo) ;=> 2
(pred-> 1 keyword? foo foo) ;=> :error-even
(pred-> 1 keyword? foo foo foo) ;=> :error-even
(pred-> 1 keyword? foo bar foo bar) ;=> 5
(pred-> 1 keyword? foo bar foo bar foo bar foo bar) ;=> :error-multiple-of-three
Your use case
A flexible choice would be to make a wrapper for validation errors
(deftype ValidationError [msg])
Then you can wrap your error code/messages as in (->ValidationError 4002) and change your threading to
(pred-> msg #(instance? ValidationError %)
msg-handler step-handler step-N sub-function)