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