Hi Yuri, You need to call .setDynamic before the access to the var is compiled. In your test, the call to .setDynamic is invoked when the function is called, which is after the function has been compiled. So when compiler sees the read of static-var the var is still static, and it can emit a read of the static var (or inline the value, I'm not sure which approach the compiler takes).
If the var is dynamic when a form is compiled that reads the var, a dynamic look-up can be emitted. Try this: (def static-var 123) (.setDynamic #'static-var true) (defn quz[] (binding [static-var 1000] (println "static-var =" static-var "deref =" @#'static-var))) (quz) ; => static-var = 1000 deref = 1000 static-var ; => 123 This doesn't help much when the var is defined in a library though, since the library will be compiled before you get a chance to set the var to be dynamic. It explains the behaviour that you are seeing though. Marc On 22 July 2015 at 13:13, Yuri Govorushchenko <yuri.go...@gmail.com> wrote: > > 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. -- 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.