Thank you for a reply, I totally agree with you on dependency injection. 
Though I'm exercising in writing a mocking framework and thought it would 
be an interesting feature to implement a thread-safe mocking of an implicit 
dependency.

среда, 22 июля 2015 г., 5:03:36 UTC+3 пользователь Surgo написал:
>
> Not that it's the answer you're looking for, but usually this is when you 
> rewrite the code you're testing to use dependency injection (ie, take the 
> var of interest as an argument instead of a global or in its lexical 
> environment).
>
> -- Morgon
>
> On Tuesday, July 21, 2015 at 10:54:42 AM UTC-4, Yuri Govorushchenko wrote:
>>
>> The problem I'm trying to solve is how to stub a variable (e.g. a 
>> function from a third-party lib) in tests so that the stubbing occurs only 
>> in the current thread with other threads continuing using var's root value. 
>> It's important because unit tests may be run in parallel. Without 
>> thread-local binding two threads stubbing the same function will lead to 
>> race conditions:
>>
>> (binding [somelib/foo foo-stub] ; throws java.lang.IllegalStateException: 
>> Can't dynamically bind non-dynamic var
>>   ; invoke tested code which depends on foo
>>   ; assert stuff
>>   )
>>
>> I've tried to use *.setDynamic* (as described in blog post [1]) but it 
>> doesn't work without direct *deref*-ing of the var:
>>
>> (def static-var 123)
>> (defn quz[]
>>   (.setDynamic #'static-var true)
>>   (binding [static-var 1000]
>>     (println "static-var =" static-var "deref =" @#'static-var)))
>>
>> (quz) ; => static-var = 123 deref = 1000
>>
>> This approach seems to be used in a recent *bolth* lib [2]. And The 
>> strange thing is that in REPL running this code line by line works:
>>
>> user=> (def static-var 123)
>>> #'user/static-var
>>> user=> (.setDynamic #'static-var true)
>>> #'user/static-var
>>> user=> (binding [static-var 1000] (println "static-var =" static-var))
>>> static-var = 1000
>>
>>
>> Looking at Var class implementation I couldn't figure out why .
>> *setDynamic* call wouldn't work. My guess is that compiler somehow 
>> caches initial static Var value for performance reasons?..
>>
>> So the questions are:
>> 1) Is it a bug that *.setDynamic* + *binding* don't work?
>> 2) Is there any other way to temporally rebind static variable 
>> thread-locally? Considering I can't add *^:dynamic* into third-party lib 
>> and don't want to write a wrapper or use dependency injection in order to 
>> explicitly substitute the dependency in my unit tests.
>> 3) Is there a Clojure parallel test runner which runs tests in new 
>> processes instead of threads? This would eliminate any race conditions. 
>> Python's *nose* test runner works this way [3].
>> 4) Maybe crazy: does Clojure allow dynamically rebinding the symbol to a 
>> new Var instance so that I could set *'static-var* to point at *(.setDynamic 
>> (Var/create)*?
>> 5) Even crazier idea: can I change the nature of the var so that it 
>> behaves like an InheritedThreadLocal instead of ThreadLocal, but without 
>> forcing a user to *deref* it (as it was described in [4])?
>>
>> Links:
>> [1] 
>> http://blog.zolotko.me/2012/06/making-variable-dynamic-in-clojure.html
>> [2] 
>> https://github.com/yeller/bolth/blob/323532683e3f66ae11566db5423c1e927e51818e/src/bolth/runner.clj#L99
>> [3] 
>> http://nose.readthedocs.org/en/latest/doc_tests/test_multiprocess/multiprocess.html
>> [4] https://aphyr.com/posts/240-configuration-and-scope  - see 
>> "Thread-inheritable dynamic vars in Clojure"
>>
>>

-- 
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/d/optout.

Reply via email to