with-redefs - clojure.core

ClojureDocs ~2 min read
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