Mercurial > lasercutter
comparison src/clojure/contrib/mock.clj.rej @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 diff a/src/main/clojure/clojure/contrib/mock.clj b/src/main/clojure/clojure/contrib/mock.clj (rejected hunks) | |
2 @@ -1,285 +1,282 @@ | |
3 -;;; clojure.contrib.mock.clj: mocking/expectation framework for Clojure | |
4 - | |
5 -;; by Matt Clark | |
6 - | |
7 -;; Copyright (c) Matt Clark, 2009. All rights reserved. The use | |
8 -;; and distribution terms for this software are covered by the Eclipse | |
9 -;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php). | |
10 -;; By using this software in any fashion, you are | |
11 -;; agreeing to be bound by the terms of this license. You must not | |
12 -;; remove this notice, or any other, from this software. | |
13 -;;------------------------------------------------------------------------------ | |
14 - | |
15 -(comment | |
16 - ;; This is a simple function mocking library I accidentally wrote as a side | |
17 - ;; effect of trying to write an opengl library in clojure. This is loosely | |
18 - ;; based on various ruby and java mocking frameworks I have used in the past | |
19 - ;; such as mockito, easymock, and whatever rspec uses. | |
20 - ;; | |
21 - ;; expect uses bindings to wrap the functions that are being tested and | |
22 - ;; then validates the invocation count at the end. The expect macro is the | |
23 - ;; main entry point and it is given a vector of binding pairs. | |
24 - ;; The first of each pair names the dependent function you want to override, | |
25 - ;; while the second is a hashmap containing the mock description, usually | |
26 - ;; created via the simple helper methods described below. | |
27 - ;; | |
28 - ;; Usage: | |
29 - ;; | |
30 - ;; there are one or more dependent functions: | |
31 - | |
32 - (defn dep-fn1 [] "time consuming calculation in 3rd party library") | |
33 - (defn dep-fn2 [x] "function with undesirable side effects while testing") | |
34 - | |
35 - ;; then we have the code under test that calls these other functions: | |
36 - | |
37 - (defn my-code-under-test [] (dep-fn1) (dep-fn2 "a") (+ 2 2)) | |
38 - | |
39 - ;; to test this code, we simply surround it with an expect macro within | |
40 - ;; the test: | |
41 - | |
42 - (expect [dep-fn1 (times 1) | |
43 - dep-fn2 (times 1 (has-args [#(= "a" %)]))] | |
44 - (my-code-under-test)) | |
45 - | |
46 - ;; When an expectation fails during execution of the function under test, | |
47 - ;; an error condition function is called with the name of the function | |
48 - ;; being mocked, the expected form and the actual value. These | |
49 - ;; error functions can be overridden to allow easy integration into | |
50 - ;; test frameworks such as test-is by reporting errors in the function | |
51 - ;; overrides. | |
52 - | |
53 - ) ;; end comment | |
54 - | |
55 -(ns clojure.contrib.mock | |
56 - ^{:author "Matt Clark", | |
57 - :doc "function mocking/expectations for Clojure" } | |
58 - (:use [clojure.contrib.seq :only (positions)] | |
59 - [clojure.contrib.def :only (defmacro-)])) | |
60 - | |
61 - | |
62 -;;------------------------------------------------------------------------------ | |
63 -;; These are the error condition functions. Override them to integrate into | |
64 -;; the test framework of your choice, or to simply customize error handling. | |
65 - | |
66 -(defn report-problem | |
67 - {:dynamic true} | |
68 - ([function expected actual] | |
69 - (report-problem function expected actual "Expectation not met.")) | |
70 - ([function expected actual message] | |
71 - (prn (str message " Function name: " function | |
72 - " expected: " expected " actual: " actual)))) | |
73 - | |
74 -(defn no-matching-function-signature | |
75 - {:dynamic true} | |
76 - [function expected actual] | |
77 - (report-problem function expected actual | |
78 - "No matching real function signature for given argument count.")) | |
79 - | |
80 -(defn unexpected-args | |
81 - {:dynamic true} | |
82 - [function expected actual i] | |
83 - (report-problem function expected actual | |
84 - (str "Argument " i " has an unexpected value for function."))) | |
85 - | |
86 -(defn incorrect-invocation-count | |
87 - {:dynamic true} | |
88 - [function expected actual] | |
89 - (report-problem function expected actual "Unexpected invocation count.")) | |
90 - | |
91 - | |
92 -;;------------------------------------------------------------------------------ | |
93 -;; Internal Functions - ignore these | |
94 - | |
95 - | |
96 -(defn- has-arg-count-match? | |
97 - "Given the sequence of accepted argument vectors for a function, | |
98 -returns true if at least one matches the given-count value." | |
99 - [arg-lists given-count] | |
100 - (some #(let [[ind] (positions #{'&} %)] | |
101 - (if ind | |
102 - (>= given-count ind) | |
103 - (= (count %) given-count))) | |
104 - arg-lists)) | |
105 - | |
106 - | |
107 -(defn has-matching-signature? | |
108 - "Calls no-matching-function-signature if no match is found for the given | |
109 -function. If no argslist meta data is available for the function, it is | |
110 -not called." | |
111 - [fn-name args] | |
112 - (let [arg-count (count args) | |
113 - arg-lists (:arglists (meta (resolve fn-name)))] | |
114 - (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count))) | |
115 - (no-matching-function-signature fn-name arg-lists args)))) | |
116 - | |
117 - | |
118 -(defn make-arg-checker | |
119 - "Creates the argument verifying function for a replaced dependency within | |
120 -the expectation bound scope. These functions take the additional argument | |
121 -of the name of the replaced function, then the rest of their args. It is | |
122 -designed to be called from the mock function generated in the first argument | |
123 -of the mock info object created by make-mock." | |
124 - [arg-preds arg-pred-forms] | |
125 - (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)] | |
126 - (fn [fn-name & args] | |
127 - (every? true? | |
128 - (map (fn [pred arg pred-form i] (if (pred arg) true | |
129 - (unexpected-args fn-name pred-form arg i))) | |
130 - sanitized-preds args arg-pred-forms (iterate inc 0)))))) | |
131 - | |
132 - | |
133 -(defn make-count-checker | |
134 - "creates the count checker that is invoked at the end of an expectation, after | |
135 -the code under test has all been executed. The function returned takes the | |
136 -name of the associated dependency and the invocation count as arguments." | |
137 - [pred pred-form] | |
138 - (let [pred-fn (if (integer? pred) #(= pred %) pred)] | |
139 - (fn [fn-name v] (if (pred-fn v) true | |
140 - (incorrect-invocation-count fn-name pred-form v))))) | |
141 - | |
142 -; Borrowed from clojure core. Remove if this ever becomes public there. | |
143 -(defmacro- assert-args | |
144 - [fnname & pairs] | |
145 - `(do (when-not ~(first pairs) | |
146 - (throw (IllegalArgumentException. | |
147 - ~(str fnname " requires " (second pairs))))) | |
148 - ~(let [more (nnext pairs)] | |
149 - (when more | |
150 - (list* `assert-args fnname more))))) | |
151 - | |
152 -(defn make-mock | |
153 - "creates a vector containing the following information for the named function: | |
154 -1. dependent function replacement - verifies signature, calls arg checker, | |
155 -increases count, returns return value. | |
156 -2. an atom containing the invocation count | |
157 -3. the invocation count checker function | |
158 -4. a symbol of the name of the function being replaced." | |
159 - [fn-name expectation-hash] | |
160 - (assert-args make-mock | |
161 - (map? expectation-hash) "a map of expectations") | |
162 - (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true)) | |
163 - count-atom (atom 0) | |
164 - ret-fn (or | |
165 - (expectation-hash :calls) | |
166 - (fn [& args] (expectation-hash :returns)))] | |
167 - [(fn [& args] | |
168 - (has-matching-signature? fn-name args) | |
169 - (apply arg-checker fn-name args) | |
170 - (swap! count-atom inc) | |
171 - (apply ret-fn args)) | |
172 - count-atom | |
173 - (or (expectation-hash :times) (fn [fn-name v] true)) | |
174 - fn-name])) | |
175 - | |
176 - | |
177 -(defn validate-counts | |
178 - "given the sequence of all mock data for the expectation, simply calls the | |
179 -count checker for each dependency." | |
180 - [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i))) | |
181 - | |
182 -(defn ^{:private true} make-bindings [expect-bindings mock-data-sym] | |
183 - `[~@(interleave (map #(first %) (partition 2 expect-bindings)) | |
184 - (map (fn [i] `(nth (nth ~mock-data-sym ~i) 0)) | |
185 - (range (quot (count expect-bindings) 2))))]) | |
186 - | |
187 - | |
188 -;;------------------------------------------------------------------------------ | |
189 -;; These are convenience functions to improve the readability and use of this | |
190 -;; library. Useful in expressions such as: | |
191 -;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc) | |
192 - | |
193 -(defn once [x] (= 1 x)) | |
194 - | |
195 -(defn never [x] (zero? x)) | |
196 - | |
197 -(defn more-than [x] #(< x %)) | |
198 - | |
199 -(defn less-than [x] #(> x %)) | |
200 - | |
201 -(defn between [x y] #(and (< x %) (> y %))) | |
202 - | |
203 - | |
204 -;;------------------------------------------------------------------------------ | |
205 -;; The following functions can be used to build up the expectation hash. | |
206 - | |
207 -(defn returns | |
208 - "Creates or associates to an existing expectation hash the :returns key with | |
209 -a value to be returned by the expectation after a successful invocation | |
210 -matching its expected arguments (if applicable). | |
211 -Usage: | |
212 -(returns ret-value expectation-hash?)" | |
213 - | |
214 - ([val] (returns val {})) | |
215 - ([val expectation-hash] (assoc expectation-hash :returns val))) | |
216 - | |
217 - | |
218 -(defn calls | |
219 - "Creates or associates to an existing expectation hash the :calls key with a | |
220 -function that will be called with the given arguments. The return value from | |
221 -this function will be returned returned by the expected function. If both this | |
222 -and returns are specified, the return value of \"calls\" will have precedence. | |
223 -Usage: | |
224 -(calls some-fn expectation-hash?)" | |
225 - | |
226 - ([val] (calls val {})) | |
227 - ([val expectation-hash] (assoc expectation-hash :calls val))) | |
228 - | |
229 - | |
230 -(defmacro has-args | |
231 - "Creates or associates to an existing expectation hash the :has-args key with | |
232 -a value corresponding to a function that will either return true if its | |
233 -argument expectations are met or throw an exception with the details of the | |
234 -first failed argument it encounters. | |
235 -Only specify as many predicates as you are interested in verifying. The rest | |
236 -of the values are safely ignored. | |
237 -Usage: | |
238 -(has-args [arg-pred-1 arg-pred-2 ... arg-pred-n] expectation-hash?)" | |
239 - | |
240 - ([arg-pred-forms] `(has-args ~arg-pred-forms {})) | |
241 - ([arg-pred-forms expect-hash-form] | |
242 - (assert-args has-args | |
243 - (vector? arg-pred-forms) "a vector of argument predicates") | |
244 - `(assoc ~expect-hash-form :has-args | |
245 - (make-arg-checker ~arg-pred-forms '~arg-pred-forms)))) | |
246 - | |
247 - | |
248 -(defmacro times | |
249 - "Creates or associates to an existing expectation hash the :times key with a | |
250 -value corresponding to a predicate function which expects an integer value. | |
251 -This function can either be specified as the first argument to times or can be | |
252 -the result of calling times with an integer argument, in which case the | |
253 -predicate will default to being an exact match. This predicate is called at | |
254 -the end of an expect expression to validate that an expected dependency | |
255 -function was called the expected number of times. | |
256 -Usage: | |
257 -(times n) | |
258 -(times #(> n %)) | |
259 -(times n expectation-hash)" | |
260 - ([times-fn] `(times ~times-fn {})) | |
261 - ([times-fn expectation-hash] | |
262 - `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn)))) | |
263 - | |
264 - | |
265 -;------------------------------------------------------------------------------- | |
266 -; The main expect macro. | |
267 -(defmacro expect | |
268 - "Use expect to redirect calls to dependent functions that are made within the | |
269 -code under test. Instead of calling the functions that would normally be used, | |
270 -temporary stubs are used, which can verify function parameters and call counts. | |
271 -Return values can also be specified as needed. | |
272 -Usage: | |
273 -(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))] | |
274 - (function-under-test a b c))" | |
275 - | |
276 - [expect-bindings & body] | |
277 - (assert-args expect | |
278 - (vector? expect-bindings) "a vector of expectation bindings" | |
279 - (even? (count expect-bindings)) | |
280 - "an even number of forms in expectation bindings") | |
281 - (let [mock-data (gensym "mock-data_")] | |
282 - `(let [~mock-data (map (fn [args#] | |
283 - (apply clojure.contrib.mock/make-mock args#)) | |
284 - ~(cons 'list (map (fn [[n m]] (vector (list 'quote n) m)) | |
285 - (partition 2 expect-bindings))))] | |
286 - (binding ~(make-bindings expect-bindings mock-data) ~@body) | |
287 - (clojure.contrib.mock/validate-counts ~mock-data) true))) | |
288 +;;; clojure.contrib.mock.clj: mocking/expectation framework for Clojure | |
289 + | |
290 +;; by Matt Clark | |
291 + | |
292 +;; Copyright (c) Matt Clark, 2009. All rights reserved. The use and | |
293 +;; distribution terms for this software are covered by the Eclipse Public | |
294 +;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can | |
295 +;; be found in the file epl-v10.html at the root of this distribution. By | |
296 +;; using this software in any fashion, you are agreeing to be bound by the | |
297 +;; terms of this license. You must not remove this notice, or any other | |
298 +;; from this software. | |
299 +;;------------------------------------------------------------------------------ | |
300 + | |
301 +(comment | |
302 + ;; Mock is a function mocking utility loosely based on various ruby and java | |
303 + ;; mocking frameworks such as mockito, easymock, and rspec yet adapted to | |
304 + ;; fit the functional style of clojure. | |
305 + ;; | |
306 + ;; Mock uses bindings to wrap the functions that are being tested and | |
307 + ;; then validates the invocation count at the end. The expect macro is the | |
308 + ;; main entry point and it is given a vector of binding pairs. | |
309 + ;; The first of each pair names the dependent function you want to override | |
310 + ;; while the second is a hashmap containing the mock description, usually | |
311 + ;; created via the simple helper methods described below. | |
312 + ;; | |
313 + ;; Usage: | |
314 + ;; | |
315 + ;; there are one or more dependent functions: | |
316 + | |
317 + (defn dep-fn1 [] "time consuming calculation in 3rd party library") | |
318 + (defn dep-fn2 [x] "function with undesirable side effects while testing") | |
319 + | |
320 + ;; then we have the code under test that calls these other functions: | |
321 + | |
322 + (defn my-code-under-test [] (dep-fn1) (dep-fn2 "a") (+ 2 2)) | |
323 + | |
324 + ;; to test this code, we simply surround it with an expect macro within | |
325 + ;; the test: | |
326 + | |
327 + (expect [dep-fn1 (times 1) | |
328 + dep-fn2 (times 1 (has-args [#(= "a" %)]))] | |
329 + (my-code-under-test)) | |
330 + | |
331 + ;; When an expectation fails during execution of the function under test | |
332 + ;; an error condition function is called with the name of the function | |
333 + ;; being mocked, the expected form and the actual value. These | |
334 + ;; error functions can be overridden to allow easy integration into | |
335 + ;; test frameworks such as test-is by reporting errors in the function | |
336 + ;; overrides. | |
337 + | |
338 + ) ;; end comment | |
339 + | |
340 +(ns clojure.contrib.mock | |
341 + ^{:author "Matt Clark" | |
342 + :doc "function mocking/expectations for Clojure" } | |
343 + (:use [clojure.contrib.seq :only (positions)] | |
344 + [clojure.contrib.def :only (defmacro-)])) | |
345 + | |
346 + | |
347 +;;------------------------------------------------------------------------------ | |
348 +;; These are the error condition functions. Override them to integrate into | |
349 +;; the test framework of your choice, or to simply customize error handling. | |
350 + | |
351 +(defn report-problem | |
352 + {:dynamic true} | |
353 + ([function expected actual] | |
354 + (report-problem function expected actual "Expectation not met.")) | |
355 + ([function expected actual message] | |
356 + (prn (str message " Function name: " function | |
357 + " expected: " expected " actual: " actual)))) | |
358 + | |
359 +(defn no-matching-function-signature | |
360 + {:dynamic true} | |
361 + [function expected actual] | |
362 + (report-problem function expected actual | |
363 + "No matching real function signature for given argument count.")) | |
364 + | |
365 +(defn unexpected-args | |
366 + {:dynamic true} | |
367 + [function expected actual i] | |
368 + (report-problem function expected actual | |
369 + (str "Argument " i " has an unexpected value for function."))) | |
370 + | |
371 +(defn incorrect-invocation-count | |
372 + {:dynamic true} | |
373 + [function expected actual] | |
374 + (report-problem function expected actual "Unexpected invocation count.")) | |
375 + | |
376 + | |
377 +;;------------------------------------------------------------------------------ | |
378 +;; Internal Functions - ignore these | |
379 + | |
380 + | |
381 +(defn- has-arg-count-match? | |
382 + "Given the sequence of accepted argument vectors for a function | |
383 +returns true if at least one matches the given-count value." | |
384 + [arg-lists given-count] | |
385 + (some #(let [[ind] (positions #{'&} %)] | |
386 + (if ind | |
387 + (>= given-count ind) | |
388 + (= (count %) given-count))) | |
389 + arg-lists)) | |
390 + | |
391 + | |
392 +(defn has-matching-signature? | |
393 + "Calls no-matching-function-signature if no match is found for the given | |
394 +function. If no argslist meta data is available for the function, it is | |
395 +not called." | |
396 + [fn-name args] | |
397 + (let [arg-count (count args) | |
398 + arg-lists (:arglists (meta (resolve fn-name)))] | |
399 + (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count))) | |
400 + (no-matching-function-signature fn-name arg-lists args)))) | |
401 + | |
402 + | |
403 +(defn make-arg-checker | |
404 + "Creates the argument verifying function for a replaced dependency within | |
405 +the expectation bound scope. These functions take the additional argument | |
406 +of the name of the replaced function, then the rest of their args. It is | |
407 +designed to be called from the mock function generated in the first argument | |
408 +of the mock info object created by make-mock." | |
409 + [arg-preds arg-pred-forms] | |
410 + (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)] | |
411 + (fn [fn-name & args] | |
412 + (every? true? | |
413 + (map (fn [pred arg pred-form i] (if (pred arg) true | |
414 + (unexpected-args fn-name | |
415 + pred-form arg i))) | |
416 + sanitized-preds args arg-pred-forms (iterate inc 0)))))) | |
417 + | |
418 + | |
419 +(defn make-count-checker | |
420 + "creates the count checker that is invoked at the end of an expectation, after | |
421 +the code under test has all been executed. The function returned takes the | |
422 +name of the associated dependency and the invocation count as arguments." | |
423 + [pred pred-form] | |
424 + (let [pred-fn (if (integer? pred) #(= pred %) pred)] | |
425 + (fn [fn-name v] (if (pred-fn v) true | |
426 + (incorrect-invocation-count fn-name pred-form v))))) | |
427 + | |
428 +(defn make-mock | |
429 + "creates a vector containing the following information for the named function: | |
430 +1. dependent function replacement - verifies signature, calls arg checker | |
431 +increases count, returns return value. | |
432 +2. an atom containing the invocation count | |
433 +3. the invocation count checker function | |
434 +4. a symbol of the name of the function being replaced." | |
435 + [fn-name expectation-hash] | |
436 + {:pre [(map? expectation-hash) | |
437 + (symbol? fn-name)]} | |
438 + (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true)) | |
439 + count-atom (atom 0) | |
440 + ret-fn (or | |
441 + (expectation-hash :calls) | |
442 + (fn [& args] (expectation-hash :returns)))] | |
443 + [(fn [& args] | |
444 + (has-matching-signature? fn-name args) | |
445 + (apply arg-checker fn-name args) | |
446 + (swap! count-atom inc) | |
447 + (apply ret-fn args)) | |
448 + count-atom | |
449 + (or (expectation-hash :times) (fn [fn-name v] true)) | |
450 + fn-name])) | |
451 + | |
452 + | |
453 +(defn validate-counts | |
454 + "given the sequence of all mock data for the expectation, simply calls the | |
455 +count checker for each dependency." | |
456 + [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i))) | |
457 + | |
458 +(defn- make-bindings [expect-bindings mock-data-sym] | |
459 + `[~@(interleave (map #(first %) (partition 2 expect-bindings)) | |
460 + (map (fn [i] `(nth (nth ~mock-data-sym ~i) 0)) | |
461 + (range (quot (count expect-bindings) 2))))]) | |
462 + | |
463 + | |
464 +;;------------------------------------------------------------------------------ | |
465 +;; These are convenience functions to improve the readability and use of this | |
466 +;; library. Useful in expressions such as: | |
467 +;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc) | |
468 + | |
469 +;; best used in the times function | |
470 +(defn once [x] (= 1 x)) | |
471 + | |
472 +(defn never [x] (zero? x)) | |
473 + | |
474 +(defn more-than [x] #(< x %)) | |
475 + | |
476 +(defn less-than [x] #(> x %)) | |
477 + | |
478 +(defn between [x y] #(and (< x %) (> y %))) | |
479 + | |
480 +;;best used in the has-args function | |
481 +(defn anything [x] true) | |
482 + | |
483 + | |
484 +;;------------------------------------------------------------------------------ | |
485 +;; The following functions can be used to build up the expectation hash. | |
486 + | |
487 +(defn returns | |
488 + "Creates or associates to an existing expectation hash the :returns key with | |
489 +a value to be returned by the expectation after a successful invocation | |
490 +matching its expected arguments (if applicable). | |
491 +Usage: | |
492 +(returns ret-value expectation-hash?)" | |
493 + | |
494 + ([val] (returns val {})) | |
495 + ([val expectation-hash] | |
496 + {:pre [(map? expectation-hash)]} | |
497 + (assoc expectation-hash :returns val))) | |
498 + | |
499 + | |
500 +(defn calls | |
501 + "Creates or associates to an existing expectation hash the :calls key with a | |
502 +function that will be called with the given arguments. The return value from | |
503 +this function will be returned by the expected function. If both this | |
504 +and returns are specified, the return value of \"calls\" will have precedence. | |
505 +Usage: | |
506 +(calls some-fn expectation-hash?)" | |
507 + | |
508 + ([val] (calls val {})) | |
509 + ([val expectation-hash] | |
510 + {:pre [(map? expectation-hash)]} | |
511 + (assoc expectation-hash :calls val))) | |
512 + | |
513 + | |
514 +(defmacro has-args | |
515 + "Creates or associates to an existing expectation hash the :has-args key with | |
516 +a value corresponding to a function that will either return true if its | |
517 +argument expectations are met or throw an exception with the details of the | |
518 +first failed argument it encounters. | |
519 +Only specify as many predicates as you are interested in verifying. The rest | |
520 +of the values are safely ignored. | |
521 +Usage: | |
522 +(has-args [arg-pred-1 arg-pred-2 ... arg-pred-n] expectation-hash?)" | |
523 + | |
524 + ([arg-pred-forms] `(has-args ~arg-pred-forms {})) | |
525 + ([arg-pred-forms expectation-hash] | |
526 + {:pre [(vector? arg-pred-forms) | |
527 + (map? expectation-hash)]} | |
528 + `(assoc ~expectation-hash :has-args | |
529 + (make-arg-checker ~arg-pred-forms '~arg-pred-forms)))) | |
530 + | |
531 + | |
532 +(defmacro times | |
533 + "Creates or associates to an existing expectation hash the :times key with a | |
534 +value corresponding to a predicate function which expects an integer value. | |
535 +Also, an integer can be specified, in which case the times will only be an | |
536 +exact match. The times check is called at the end of an expect expression to | |
537 +validate that an expected dependency function was called the expected | |
538 +number of times. | |
539 +Usage: | |
540 +(times n) | |
541 +(times #(> n %)) | |
542 +(times n expectation-hash)" | |
543 + ([times-fn] `(times ~times-fn {})) | |
544 + ([times-fn expectation-hash] | |
545 + {:pre [(map? expectation-hash)]} | |
546 + `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn)))) | |
547 + | |
548 + | |
549 +;------------------------------------------------------------------------------- | |
550 +; The main expect macro. | |
551 +(defmacro expect | |
552 + "Use expect to redirect calls to dependent functions that are made within the | |
553 +code under test. Instead of calling the functions that would normally be used | |
554 +temporary stubs are used, which can verify function parameters and call counts. | |
555 +Return values of overridden functions can also be specified as needed. | |
556 +Usage: | |
557 +(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))] | |
558 + (function-under-test a b c))" | |
559 + | |
560 + [expect-bindings & body] | |
561 + {:pre [(vector? expect-bindings) | |
562 + (even? (count expect-bindings))]} | |
563 + (let [mock-data (gensym "mock-data_")] | |
564 + `(let [~mock-data (map (fn [args#] | |
565 + (apply clojure.contrib.mock/make-mock args#)) | |
566 + ~(cons 'list (map (fn [[n m]] (vector (list 'quote n) m)) | |
567 + (partition 2 expect-bindings))))] | |
568 + (binding ~(make-bindings expect-bindings mock-data) ~@body) | |
569 + (clojure.contrib.mock/validate-counts ~mock-data) true))) |