Sorry, in the last illustration, the (binding [*deps* deps] ...) cannot be 
useful for Compojure route handlers because dynamic vars are bound at a 
thread-local level; you will probably have to `alter-var-root` it to some 
var and have the handlers use that static var instead. In the code I write 
personally, I use a static var that I `alter-var-root` so I couldn't see 
the error in dynamic var immediately.

Shantanu

On Wednesday, 27 March 2013 12:16:58 UTC+5:30, Shantanu Kumar wrote:
>
>
>
> On Wednesday, 27 March 2013 08:06:30 UTC+5:30, Leif wrote:
>>
>> Hi, Shantanu.
>>
>> Thanks for the suggestions.  A couple thoughts:
>>
>> 1. Many times, I seem to stub or mock things that are scattered here and 
>> there in the code, like things that send email or log metrics, etc.  so 
>> they are not really isolated (or isolatable??), but I still want to test 
>> that they get called.
>> 2. So I'm usually mocking utility functions that have side effects.  Am I 
>> mocking at the right level?
>> 3. For these types of things, even if I make protocols for them, passing 
>> them all in as arguments seems like it could get messy fast.  E.g. my 
>> function used to be (f x), but now it's (f x logger emailer metric-logger 
>> ...)
>> 4. I'm not really familiar with the methods you describe, so any 
>> elaboration on your comments would be welcome.  Or even better, a link to a 
>> project that uses the designs you mentioned.
>>
>
> I do not have any Open Source application using this technique to share 
> now, but I will try to illustrate here. If you have a Ring-based web app, 
> you can initialize the app in a middleware, or if you have a command-line 
> app then you can initialize in the -main function. The initialization 
> function can return a map of mockable implementations, which could be 
> either protocol implementations or functions:
>
> (def ^:dynamic *deps* nil)
>
> ;; in the Ring middleware or -main fn (using prod config)
> (let [deps {:logger (make-logger app-config)     ; returns fn
>                  :emailer (make-emailer app-config)  ; returns fn
>                  :db-worker (make-db-worker)           ; IDatabase 
> protocol impl
>                  :metric-logger (make-metric-logger app-config)}]
>   (binding [*deps* deps] ; compojure route handlers can use *deps*
>     ...))
>
> So, how should the regular functions be defined now? We need to propagate 
> the dependencies to all the places that need it.
>
> Earlier version:
>
> ;; notice the dependencies: logger, db, email
> (defn foo [x]
>   (util/logger :debug "Enter")
>   (let [p (make-db-payload x)]
>     (db/write p))
>   (let [m (compose-mail "xyz")]
>     (util/email m))
>   (util/logger :debug "Exit"))
>
> New version:
>
> ;; deps can be actual services or mocked
> (defn foo [x deps]
>   ((:logger deps) :debug "Enter")
>   (let [p (make-db-payload x)]
>     (.write (:db-worker deps) p))
>   (let [m (compose-mail "xyz")]
>     ((:emailer deps) m))
>   ((:logger deps) :debug "Exit"))
>
> This can be streamlined by extracting snippets like (:logger deps) into 
> dedicated functions to avoid typos. But yeah, the idea is that deps needs 
> to be carried around. In the places where you need to mock something, 
> just call as follows:
>
> (let [deps (make-mock-deps)]
>   (foo x deps))
>
> So, to answer your question #2, in this approach the code has an 
> inside-out design and works at a higher order.
>
> Shantanu
>

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to