open access

July 31, 2012

Sometimes you might run into a situation where you're using a library which defines a certain function in a way that might not work the way you need it to in a particular context. To make things worse, this function might be used by the library internally, so you can't simply write your own version and use it.

In some languages it's possible to use monkey patching to get around this problem. This approach allows you to simply redefine the offending function at runtime with your own version. The downside of this approach is that the change is global and as such might interact poorly with other code which expects the original version.

In Clojure it's possible redefine an existing function in a particular context using with-redefs. This approach gives us the ability to make runtime modifications in a safer fashion where we know exactly what code is affected.

Let's look at an example where we have a get-data function defined in namespace foo which is used by display-results in namespace bar. When we write tests for bar we would like to use preset test data instead of calling out to the database:

(ns foo)

(defn get-data []
  ;gets some data from a db
  )

(ns bar
 (:require foo))

(defn display-results []
  (apply str (interpose ", " (foo/get-data))))

(ns tests
  (:use clojure.test)
  (:require foo bar))

(deftest display-results-test
  (with-redefs [foo/get-data (fn[] ["Doe", "John"])]
    (is (= "Doe, John" (bar/display-results)))))
 
Now any code that references foo/get-data inside the with-redefs scope will get ["Doe", "John"] as a result.

Copyright © Dmitri Sotnikov

Powered by Cryogen