with-redefs - clojure.core
View original- current
Summary (TL;DR)
with-redefs temporarily changes the root value of Vars during execution of body, restoring old values after. Changes are visible in all threads. Useful for mocking in tests. Example: (with-redefs [foo 2] foo) returns 2. Be careful with parallelism; concurrent use can permanently alter a Var. Does not work on macros or inlined functions. Redefs happen in parallel, so one redef cannot reference another's new value.
- (with-redefs bindings & body)
binding => var-symbol temp-value-expr Temporarily redefines Vars while executing the body. The temp-value-exprs will be evaluated and each resulting value will replace in parallel the root value of its Var. After the body is executed, the root values of all the Vars will be set back to their old values. These temporary changes will be visible in all threads. Useful for mocking out functions during testing.
7 Examples
user=> [(type []) (class [])] [clojure.lang.PersistentVector clojure.lang.PersistentVector] user=> (with-redefs [type (constantly java.lang.String) class (constantly 10)] [(type []) (class [])]) [java.lang.String 10]
(ns http) (defn post [url] {:body "Hello world"}) (ns app (:require [clojure.test :refer [deftest is run-tests]])) (deftest is-a-macro (with-redefs [http/post (fn [url] {:body "Goodbye world"})] (is (= {:body "Goodbye world"} (http/post "http://service.com/greet"))))) (run-tests) ;; test is passing
;; be careful, with-redefs can permanently change a var if applied concurrently: user> (defn ten [] 10) #'user/ten user> (doall (pmap #(with-redefs [ten (fn [] %)] (ten)) (range 20 100))) ... user> (ten) 79
;; redefine var (def foo 1) #'user/foo (with-redefs [foo 2] foo) 2 ;; redefine private var (ns first) (def ^:private foo 1) #'first/foo (ns second) (with-redefs [first/foo 2] @#'first/foo) 2 ;; @#' is the macros of (deref (var first/foo)) (with-redefs [first/foo 2] (deref (var first/foo)) 2
;; Vars return to their previous value outside of `with-redefs` body (with-redefs [first last] (first [1 2])) ;; => 2 ((with-redefs [first last] #(first [1 2]))) ;; => 1
;; Macros are not stored in vars, so `with-redefs` doesn't work on them. (defmacro a [] 1) => #'user/a (a) => 1 (with-redefs [a (constantly 2)] (a)) => 1 ;; Inlined functions are also not affected in their inlined form: (defn b {:inline (fn [_] 1)} [_] 1) (with-redefs [b (constantly 2)] (b :foo)) => 1 ;; but their non-inlined form will be affected: (with-redefs [b (constantly 2)] (map b [:foo :bar])) => (2 2)
;; "…each resulting value will replace in parallel the root value of its Var." ;; "parallel" means redefs can't reference each other: (def foo :f) (def bar :b) (with-redefs [foo :e, bar foo] bar) ;; => :f ;; n.b. bar gets foo's original value, not its redeffed value
Log in to add an example
See Also
Temporarily redefines Vars during a call to func. Each val of binding-map will replace the root v...
Atomically alters the root binding of var v by applying f to its current value plus any args
Takes a map of Var/value pairs. Installs for the given Vars the associated values as thread-local ...
Log in to add a see-also
0 Notes
No notes for
with-redefs
Log in to add a note







