aboutsummaryrefslogtreecommitdiff
path: root/src/clojure/contrib/mock.clj
diff options
context:
space:
mode:
Diffstat (limited to 'src/clojure/contrib/mock.clj')
-rw-r--r--src/clojure/contrib/mock.clj285
1 files changed, 0 insertions, 285 deletions
diff --git a/src/clojure/contrib/mock.clj b/src/clojure/contrib/mock.clj
deleted file mode 100644
index a284b527..00000000
--- a/src/clojure/contrib/mock.clj
+++ /dev/null
@@ -1,285 +0,0 @@
-;;; clojure.contrib.mock.clj: mocking/expectation framework for Clojure
-
-;; by Matt Clark
-
-;; Copyright (c) Matt Clark, 2009. All rights reserved. The use
-;; and distribution terms for this software are covered by the Eclipse
-;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php).
-;; By using this software in any fashion, you are
-;; agreeing to be bound by the terms of this license. You must not
-;; remove this notice, or any other, from this software.
-;;------------------------------------------------------------------------------
-
-(comment
- ;; This is a simple function mocking library I accidentally wrote as a side
- ;; effect of trying to write an opengl library in clojure. This is loosely
- ;; based on various ruby and java mocking frameworks I have used in the past
- ;; such as mockito, easymock, and whatever rspec uses.
- ;;
- ;; expect 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 test-is by reporting errors in the function
- ;; overrides.
-
- ) ;; end comment
-
-(ns clojure.contrib.mock
- #^{:author "Matt Clark",
- :doc "function mocking/expectations for Clojure" }
- (:use [clojure.contrib.seq-utils :only (positions)]
- [clojure.contrib.def :only (defmacro-)]))
-
-
-;;------------------------------------------------------------------------------
-;; These are the error condition functions. Override them to integrate into
-;; the test framework of your choice, or to simply customize error handling.
-
-(defn report-problem
- {:dynamic true}
- ([function expected actual]
- (report-problem function expected actual "Expectation not met."))
- ([function expected actual message]
- (prn (str message " Function name: " function
- " expected: " expected " actual: " actual))))
-
-(defn no-matching-function-signature
- {:dynamic true}
- [function expected actual]
- (report-problem function expected actual
- "No matching real function signature for given argument count."))
-
-(defn unexpected-args
- {:dynamic true}
- [function expected actual i]
- (report-problem function expected actual
- (str "Argument " i " has an unexpected value for function.")))
-
-(defn incorrect-invocation-count
- {:dynamic true}
- [function expected actual]
- (report-problem function expected actual "Unexpected invocation count."))
-
-
-;;------------------------------------------------------------------------------
-;; Internal Functions - ignore these
-
-
-(defn- has-arg-count-match?
- "Given the sequence of accepted argument vectors for a function,
-returns true if at least one matches the given-count value."
- [arg-lists given-count]
- (some #(let [[ind] (positions #{'&} %)]
- (if ind
- (>= given-count ind)
- (= (count %) given-count)))
- arg-lists))
-
-
-(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
-not called."
- [fn-name args]
- (let [arg-count (count args)
- arg-lists (:arglists (meta (resolve fn-name)))]
- (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count)))
- (no-matching-function-signature fn-name arg-lists args))))
-
-
-(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."
- [arg-preds arg-pred-forms]
- (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)]
- (fn [fn-name & args]
- (every? true?
- (map (fn [pred arg pred-form i] (if (pred arg) true
- (unexpected-args fn-name pred-form arg i)))
- sanitized-preds args arg-pred-forms (iterate inc 0))))))
-
-
-(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."
- [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)))))
-
-; Borrowed from clojure core. Remove if this ever becomes public there.
-(defmacro- assert-args
- [fnname & pairs]
- `(do (when-not ~(first pairs)
- (throw (IllegalArgumentException.
- ~(str fnname " requires " (second pairs)))))
- ~(let [more (nnext pairs)]
- (when more
- (list* `assert-args fnname more)))))
-
-(defn make-mock
- "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."
- [fn-name expectation-hash]
- (assert-args make-mock
- (map? expectation-hash) "a map of expectations")
- (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true))
- count-atom (atom 0)
- ret-fn (or
- (expectation-hash :calls)
- (fn [& args] (expectation-hash :returns)))]
- [(fn [& args]
- (has-matching-signature? fn-name args)
- (apply arg-checker fn-name args)
- (swap! count-atom inc)
- (apply ret-fn args))
- count-atom
- (or (expectation-hash :times) (fn [fn-name v] true))
- fn-name]))
-
-
-(defn validate-counts
- "given the sequence of all mock data for the expectation, simply calls the
-count checker for each dependency."
- [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i)))
-
-(defn #^{:private true} make-bindings [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))))])
-
-
-;;------------------------------------------------------------------------------
-;; These are convenience functions to improve the readability and use of this
-;; library. Useful in expressions such as:
-;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc)
-
-(defn once [x] (= 1 x))
-
-(defn never [x] (zero? x))
-
-(defn more-than [x] #(< x %))
-
-(defn less-than [x] #(> x %))
-
-(defn between [x y] #(and (< x %) (> y %)))
-
-
-;;------------------------------------------------------------------------------
-;; The following functions can be used to build up the expectation hash.
-
-(defn 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?)"
-
- ([val] (returns val {}))
- ([val expectation-hash] (assoc expectation-hash :returns val)))
-
-
-(defn 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 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?)"
-
- ([val] (calls val {}))
- ([val expectation-hash] (assoc expectation-hash :calls val)))
-
-
-(defmacro 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?)"
-
- ([arg-pred-forms] `(has-args ~arg-pred-forms {}))
- ([arg-pred-forms expect-hash-form]
- (assert-args has-args
- (vector? arg-pred-forms) "a vector of argument predicates")
- `(assoc ~expect-hash-form :has-args
- (make-arg-checker ~arg-pred-forms '~arg-pred-forms))))
-
-
-(defmacro 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.
-This function can either be specified as the first argument to times or can be
-the result of calling times with an integer argument, in which case the
-predicate will default to being an exact match. This predicate 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)"
- ([times-fn] `(times ~times-fn {}))
- ([times-fn expectation-hash]
- `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn))))
-
-
-;-------------------------------------------------------------------------------
-; The main expect macro.
-(defmacro expect
- "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 can also be specified as needed.
-Usage:
-(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))]
- (function-under-test a b c))"
-
- [expect-bindings & body]
- (assert-args expect
- (vector? expect-bindings) "a vector of expectation bindings"
- (even? (count expect-bindings))
- "an even number of forms in expectation bindings")
- (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))
- (partition 2 expect-bindings))))]
- (binding ~(make-bindings expect-bindings mock-data) ~@body)
- (clojure.contrib.mock/validate-counts ~mock-data) true)))