Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

update mock to use with-redefs

documentation is also updated in this commit to make it more idiomatic.
  • Loading branch information...
commit 0aad2e97125c2d6d6c8227b1425386f1f09baf9e 1 parent 1a88c88
@mclark mclark authored
View
154 modules/mock/src/main/clojure/clojure/contrib/mock.clj
@@ -11,46 +11,13 @@
;; from this software.
;;------------------------------------------------------------------------------
-(comment
- ;; Mock uses bindings to wrap the functions that are being tested and
- ;; then validates the invocation count at the end. The expect macro is the
- ;; main entry point and it is given a vector of binding pairs.
- ;; The first of each pair names the dependent function you want to override
- ;; while the second is a hashmap containing the mock description, usually
- ;; created via the simple helper methods described below.
- ;;
- ;; Usage:
- ;;
- ;; there are one or more dependent functions:
-
- (defn dep-fn1 [] "time consuming calculation in 3rd party library")
- (defn dep-fn2 [x] "function with undesirable side effects while testing")
-
- ;; then we have the code under test that calls these other functions:
-
- (defn my-code-under-test [] (dep-fn1) (dep-fn2 "a") (+ 2 2))
-
- ;; to test this code, we simply surround it with an expect macro within
- ;; the test:
-
- (expect [dep-fn1 (times 1)
- dep-fn2 (times 1 (has-args [#(= "a" %)]))]
- (my-code-under-test))
-
- ;; When an expectation fails during execution of the function under test
- ;; an error condition function is called with the name of the function
- ;; being mocked, the expected form and the actual value. These
- ;; error functions can be overridden to allow easy integration into
- ;; test frameworks such as clojure.test by reporting errors in the function
- ;; overrides.
-
- ) ;; end comment
-
(ns clojure.contrib.mock
^{:author "Matt Clark"
:doc "Mock is a function mocking utility inspired by the various ruby and
java mocking frameworks such as mockito, easymock, and rspec yet designed to
-fit the functional style of clojure."}
+fit the functional style of clojure."
+ :see-also [["Mocking with clojure.contrib.mock"
+ "http://notesonclojure.blogspot.com/2010/06/mocking-with-clojurecontribmock.html"]]}
(:use [clojure.contrib.seq :only (positions)]))
@@ -59,7 +26,9 @@ fit the functional style of clojure."}
;; the test framework of your choice, or to simply customize error handling.
(defn report-problem
- {:dynamic true}
+ {:dynamic true
+ :doc "Override this to customize error handling. By default, prints an
+error message to *out*."}
([function expected actual]
(report-problem function expected actual "Expectation not met."))
([function expected actual message]
@@ -67,19 +36,25 @@ fit the functional style of clojure."}
" expected: " expected " actual: " actual))))
(defn no-matching-function-signature
- {:dynamic true}
+ {:dynamic true
+ :doc "Override to customize unmatched function signature errors.
+Prints an error message to *out* by default."}
[function expected actual]
(report-problem function expected actual
"No matching real function signature for given argument count."))
(defn unexpected-args
- {:dynamic true}
+ {:dynamic true
+ :doc "Override to customize unexpected argument errors. Prints an error
+message to *out* by default."}
[function expected actual i]
(report-problem function expected actual
(str "Argument " i " has an unexpected value for function.")))
(defn incorrect-invocation-count
- {:dynamic true}
+ {:dynamic true
+ :doc "Override to customize incorrect invocation counts. Prints an error
+to *out* by default."}
[function expected actual]
(report-problem function expected actual "Unexpected invocation count."))
@@ -89,8 +64,9 @@ fit the functional style of clojure."}
(defn- has-arg-count-match?
- "Given the sequence of accepted argument vectors for a function
+ {:doc "Given the sequence of accepted argument vectors for a function
returns true if at least one matches the given-count value."
+ :skip-wiki true}
[arg-lists given-count]
(some #(let [[ind] (positions #{'&} %)]
(if ind
@@ -100,9 +76,10 @@ returns true if at least one matches the given-count value."
(defn has-matching-signature?
- "Calls no-matching-function-signature if no match is found for the given
-function. If no argslist meta data is available for the function, it is
+ ^{:doc "Calls no-matching-function-signature if no match is found for the
+given function. If no argslist meta data is available for the function, it is
not called."
+ :skip-wiki true}
[fn-name args]
(let [arg-count (count args)
arg-lists (:arglists (meta (resolve fn-name)))]
@@ -111,11 +88,12 @@ not called."
(defn make-arg-checker
- "Creates the argument verifying function for a replaced dependency within
-the expectation bound scope. These functions take the additional argument
-of the name of the replaced function, then the rest of their args. It is
-designed to be called from the mock function generated in the first argument
-of the mock info object created by make-mock."
+ ^{:doc "Creates the argument verifying function for a replaced dependency
+within the expectation bound scope. These functions take the additional
+argument of the name of the replaced function, then the rest of their args.
+It is designed to be called from the mock function generated in the first
+argument of the mock info object created by make-mock."
+ :skip-wiki true}
[arg-preds arg-pred-forms]
(let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)]
(fn [fn-name & args]
@@ -127,21 +105,25 @@ of the mock info object created by make-mock."
(defn make-count-checker
- "creates the count checker that is invoked at the end of an expectation, after
-the code under test has all been executed. The function returned takes the
-name of the associated dependency and the invocation count as arguments."
+ ^{:doc "creates the count checker that is invoked at the end of an
+expectation, after the code under test has all been executed. The function
+returned takes the name of the associated dependency and the invocation
+count as arguments."
+ :skip-wiki true}
[pred pred-form]
(let [pred-fn (if (integer? pred) #(= pred %) pred)]
(fn [fn-name v] (if (pred-fn v) true
(incorrect-invocation-count fn-name pred-form v)))))
(defn make-mock
- "creates a vector containing the following information for the named function:
+ ^{:doc "creates a vector containing the following information for the
+named function:
1. dependent function replacement - verifies signature, calls arg checker
increases count, returns return value.
2. an atom containing the invocation count
3. the invocation count checker function
4. a symbol of the name of the function being replaced."
+ :skip-wiki true}
[fn-name expectation-hash]
{:pre [(map? expectation-hash)
(symbol? fn-name)]}
@@ -161,11 +143,14 @@ increases count, returns return value.
(defn validate-counts
- "given the sequence of all mock data for the expectation, simply calls the
-count checker for each dependency."
+ ^{:doc "given the sequence of all mock data for the expectation, simply
+calls the count checker for each dependency."
+ :skip-wiki true}
[mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i)))
-(defn- make-bindings [expect-bindings mock-data-sym]
+(defn- make-bindings
+ ^{:skip-wiki true}
+ [expect-bindings mock-data-sym]
`[~@(interleave (map #(first %) (partition 2 expect-bindings))
(map (fn [i] `(nth (nth ~mock-data-sym ~i) 0))
(range (quot (count expect-bindings) 2))))])
@@ -177,42 +162,44 @@ count checker for each dependency."
;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc)
;; best used in the times function
-(defn once [x] (= 1 x))
+(defn once "Predicate which ensures argument equals 1. For times function."
+ [x] (= 1 x))
-(defn never [x] (zero? x))
+(defn never "Predicate which ensures argument is zero. For times function."
+ [x] (zero? x))
-(defn more-than [x] #(< x %))
+(defn more-than "Returns a predicate which ensures argument is more than x."
+ [x] #(< x %))
-(defn less-than [x] #(> x %))
+(defn less-than "Returns a predicate which ensures argument is less than x."
+ [x] #(> x %))
-(defn between [x y] #(and (< x %) (> y %)))
+(defn between "Returns a predicate which ensures argument is between x and y."
+ [x y] #(and (< x %) (> y %)))
-;;best used in the has-args function
-(defn anything [x] true)
+(defn anything "Syntactic sugar for has-args function. A predicate which
+always returns true."
+ [x] true)
;;------------------------------------------------------------------------------
;; The following functions can be used to build up the expectation hash.
-(defn returns
+(defn ^{:arglists '([ret-value expectation-hash?])} returns
"Creates or associates to an existing expectation hash the :returns key with
a value to be returned by the expectation after a successful invocation
-matching its expected arguments (if applicable).
-Usage:
-(returns ret-value expectation-hash?)"
+matching its expected arguments (if applicable)."
([val] (returns val {}))
([val expectation-hash]
(assoc expectation-hash :returns val)))
-(defn calls
+(defn ^{:arglists '([replacement-fn expectation-hash?])} calls
"Creates or associates to an existing expectation hash the :calls key with a
function that will be called with the given arguments. The return value from
this function will be returned by the expected function. If both this
-and :returns are specified, the return value of :calls will have precedence.
-Usage:
-(calls some-fn expectation-hash?)"
+and :returns are specified, the return value of :calls will have precedence."
([val] (calls val {}))
([val expectation-hash]
@@ -220,15 +207,13 @@ Usage:
(assoc expectation-hash :calls val)))
-(defmacro has-args
+(defmacro ^{:arglists '([pred-coll expectation-hash?])} has-args
"Creates or associates to an existing expectation hash the :has-args key with
a value corresponding to a function that will either return true if its
argument expectations are met or throw an exception with the details of the
first failed argument it encounters.
Only specify as many predicates as you are interested in verifying. The rest
-of the values are safely ignored.
-Usage:
-(has-args [arg-pred-1 arg-pred-2 ... arg-pred-n] expectation-hash?)"
+of the values are safely ignored."
([arg-pred-forms] `(has-args ~arg-pred-forms {}))
([arg-pred-forms expectation-hash]
@@ -237,17 +222,14 @@ Usage:
(make-arg-checker ~arg-pred-forms '~arg-pred-forms))))
-(defmacro times
+(defmacro ^{:arglists '([n expectation-hash?])} times
"Creates or associates to an existing expectation hash the :times key with a
value corresponding to a predicate function which expects an integer value.
Also, an integer can be specified, in which case the times will only be an
exact match. The times check is called at the end of an expect expression to
validate that an expected dependency function was called the expected
-number of times.
-Usage:
-(times n)
-(times #(> n %))
-(times n expectation-hash)"
+number of times."
+
([times-fn] `(times ~times-fn {}))
([times-fn expectation-hash]
`(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn))))
@@ -259,10 +241,7 @@ Usage:
"Use expect to redirect calls to dependent functions that are made within the
code under test. Instead of calling the functions that would normally be used
temporary stubs are used, which can verify function parameters and call counts.
-Return values of overridden functions can also be specified as needed.
-Usage:
-(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))]
- (function-under-test a b c))"
+Return values of overridden functions can also be specified as needed."
[expect-bindings & body]
{:pre [(vector? expect-bindings)
@@ -270,7 +249,8 @@ Usage:
(let [mock-data (gensym "mock-data_")]
`(let [~mock-data (map (fn [args#]
(apply clojure.contrib.mock/make-mock args#))
- ~(cons 'list (map (fn [[n m]] (vector (list 'quote n) m))
+ ~(cons 'list (map (fn [[n m]]
+ (vector (list 'quote n) m))
(partition 2 expect-bindings))))]
- (binding ~(make-bindings expect-bindings mock-data) ~@body)
- (clojure.contrib.mock/validate-counts ~mock-data) true)))
+ (with-redefs ~(make-bindings expect-bindings mock-data) ~@body)
+ (clojure.contrib.mock/validate-counts ~mock-data) true)))
View
10 modules/mock/src/test/clojure/clojure/contrib/test_mock.clj
@@ -3,14 +3,14 @@
(:require [clojure.contrib.mock :as mock]))
; Used as dummy dependency functions
-(defn fn1 {:dynamic true} [x] :ignore)
-(defn fn2 {:dynamic true} [x y] :ignore)
-(defn fn3 {:dynamic true} ([x] :ignore)
+(defn fn1 [x] :ignore)
+(defn fn2 [x y] :ignore)
+(defn fn3 ([x] :ignore)
([x y z] :ignore))
-(defn fn4 {:dynamic true} [x y & r] :ignore)
+(defn fn4 [x y & r] :ignore)
;functions created using fn directly lack the argslist meta data
-(def #^{:dynamic true} deffed-differently (fn [x] :ignore))
+(def deffed-differently (fn [x] :ignore))
(defmacro assert-called [fn-name called? & body]
`(let [called-status?# (atom false)]
Please sign in to comment.
Something went wrong with that request. Please try again.