Mercurial > lasercutter
comparison src/clojure/test.clj @ 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 ; Copyright (c) Rich Hickey. All rights reserved. | |
2 ; The use and distribution terms for this software are covered by the | |
3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
4 ; which can be found in the file epl-v10.html at the root of this distribution. | |
5 ; By using this software in any fashion, you are agreeing to be bound by | |
6 ; the terms of this license. | |
7 ; You must not remove this notice, or any other, from this software. | |
8 | |
9 ;;; test.clj: test framework for Clojure | |
10 | |
11 ;; by Stuart Sierra | |
12 ;; March 28, 2009 | |
13 | |
14 ;; Thanks to Chas Emerick, Allen Rohner, and Stuart Halloway for | |
15 ;; contributions and suggestions. | |
16 | |
17 (ns | |
18 ^{:author "Stuart Sierra, with contributions and suggestions by | |
19 Chas Emerick, Allen Rohner, and Stuart Halloway", | |
20 :doc "A unit testing framework. | |
21 | |
22 ASSERTIONS | |
23 | |
24 The core of the library is the \"is\" macro, which lets you make | |
25 assertions of any arbitrary expression: | |
26 | |
27 (is (= 4 (+ 2 2))) | |
28 (is (instance? Integer 256)) | |
29 (is (.startsWith \"abcde\" \"ab\")) | |
30 | |
31 You can type an \"is\" expression directly at the REPL, which will | |
32 print a message if it fails. | |
33 | |
34 user> (is (= 5 (+ 2 2))) | |
35 | |
36 FAIL in (:1) | |
37 expected: (= 5 (+ 2 2)) | |
38 actual: (not (= 5 4)) | |
39 false | |
40 | |
41 The \"expected:\" line shows you the original expression, and the | |
42 \"actual:\" shows you what actually happened. In this case, it | |
43 shows that (+ 2 2) returned 4, which is not = to 5. Finally, the | |
44 \"false\" on the last line is the value returned from the | |
45 expression. The \"is\" macro always returns the result of the | |
46 inner expression. | |
47 | |
48 There are two special assertions for testing exceptions. The | |
49 \"(is (thrown? c ...))\" form tests if an exception of class c is | |
50 thrown: | |
51 | |
52 (is (thrown? ArithmeticException (/ 1 0))) | |
53 | |
54 \"(is (thrown-with-msg? c re ...))\" does the same thing and also | |
55 tests that the message on the exception matches the regular | |
56 expression re: | |
57 | |
58 (is (thrown-with-msg? ArithmeticException #\"Divide by zero\" | |
59 (/ 1 0))) | |
60 | |
61 DOCUMENTING TESTS | |
62 | |
63 \"is\" takes an optional second argument, a string describing the | |
64 assertion. This message will be included in the error report. | |
65 | |
66 (is (= 5 (+ 2 2)) \"Crazy arithmetic\") | |
67 | |
68 In addition, you can document groups of assertions with the | |
69 \"testing\" macro, which takes a string followed by any number of | |
70 assertions. The string will be included in failure reports. | |
71 Calls to \"testing\" may be nested, and all of the strings will be | |
72 joined together with spaces in the final report, in a style | |
73 similar to RSpec <http://rspec.info/> | |
74 | |
75 (testing \"Arithmetic\" | |
76 (testing \"with positive integers\" | |
77 (is (= 4 (+ 2 2))) | |
78 (is (= 7 (+ 3 4)))) | |
79 (testing \"with negative integers\" | |
80 (is (= -4 (+ -2 -2))) | |
81 (is (= -1 (+ 3 -4))))) | |
82 | |
83 Note that, unlike RSpec, the \"testing\" macro may only be used | |
84 INSIDE a \"deftest\" or \"with-test\" form (see below). | |
85 | |
86 | |
87 DEFINING TESTS | |
88 | |
89 There are two ways to define tests. The \"with-test\" macro takes | |
90 a defn or def form as its first argument, followed by any number | |
91 of assertions. The tests will be stored as metadata on the | |
92 definition. | |
93 | |
94 (with-test | |
95 (defn my-function [x y] | |
96 (+ x y)) | |
97 (is (= 4 (my-function 2 2))) | |
98 (is (= 7 (my-function 3 4)))) | |
99 | |
100 As of Clojure SVN rev. 1221, this does not work with defmacro. | |
101 See http://code.google.com/p/clojure/issues/detail?id=51 | |
102 | |
103 The other way lets you define tests separately from the rest of | |
104 your code, even in a different namespace: | |
105 | |
106 (deftest addition | |
107 (is (= 4 (+ 2 2))) | |
108 (is (= 7 (+ 3 4)))) | |
109 | |
110 (deftest subtraction | |
111 (is (= 1 (- 4 3))) | |
112 (is (= 3 (- 7 4)))) | |
113 | |
114 This creates functions named \"addition\" and \"subtraction\", which | |
115 can be called like any other function. Therefore, tests can be | |
116 grouped and composed, in a style similar to the test framework in | |
117 Peter Seibel's \"Practical Common Lisp\" | |
118 <http://www.gigamonkeys.com/book/practical-building-a-unit-test-framework.html> | |
119 | |
120 (deftest arithmetic | |
121 (addition) | |
122 (subtraction)) | |
123 | |
124 The names of the nested tests will be joined in a list, like | |
125 \"(arithmetic addition)\", in failure reports. You can use nested | |
126 tests to set up a context shared by several tests. | |
127 | |
128 | |
129 RUNNING TESTS | |
130 | |
131 Run tests with the function \"(run-tests namespaces...)\": | |
132 | |
133 (run-tests 'your.namespace 'some.other.namespace) | |
134 | |
135 If you don't specify any namespaces, the current namespace is | |
136 used. To run all tests in all namespaces, use \"(run-all-tests)\". | |
137 | |
138 By default, these functions will search for all tests defined in | |
139 a namespace and run them in an undefined order. However, if you | |
140 are composing tests, as in the \"arithmetic\" example above, you | |
141 probably do not want the \"addition\" and \"subtraction\" tests run | |
142 separately. In that case, you must define a special function | |
143 named \"test-ns-hook\" that runs your tests in the correct order: | |
144 | |
145 (defn test-ns-hook [] | |
146 (arithmetic)) | |
147 | |
148 | |
149 OMITTING TESTS FROM PRODUCTION CODE | |
150 | |
151 You can bind the variable \"*load-tests*\" to false when loading or | |
152 compiling code in production. This will prevent any tests from | |
153 being created by \"with-test\" or \"deftest\". | |
154 | |
155 | |
156 FIXTURES (new) | |
157 | |
158 Fixtures allow you to run code before and after tests, to set up | |
159 the context in which tests should be run. | |
160 | |
161 A fixture is just a function that calls another function passed as | |
162 an argument. It looks like this: | |
163 | |
164 (defn my-fixture [f] | |
165 Perform setup, establish bindings, whatever. | |
166 (f) Then call the function we were passed. | |
167 Tear-down / clean-up code here. | |
168 ) | |
169 | |
170 Fixtures are attached to namespaces in one of two ways. \"each\" | |
171 fixtures are run repeatedly, once for each test function created | |
172 with \"deftest\" or \"with-test\". \"each\" fixtures are useful for | |
173 establishing a consistent before/after state for each test, like | |
174 clearing out database tables. | |
175 | |
176 \"each\" fixtures can be attached to the current namespace like this: | |
177 (use-fixtures :each fixture1 fixture2 ...) | |
178 The fixture1, fixture2 are just functions like the example above. | |
179 They can also be anonymous functions, like this: | |
180 (use-fixtures :each (fn [f] setup... (f) cleanup...)) | |
181 | |
182 The other kind of fixture, a \"once\" fixture, is only run once, | |
183 around ALL the tests in the namespace. \"once\" fixtures are useful | |
184 for tasks that only need to be performed once, like establishing | |
185 database connections, or for time-consuming tasks. | |
186 | |
187 Attach \"once\" fixtures to the current namespace like this: | |
188 (use-fixtures :once fixture1 fixture2 ...) | |
189 | |
190 | |
191 SAVING TEST OUTPUT TO A FILE | |
192 | |
193 All the test reporting functions write to the var *test-out*. By | |
194 default, this is the same as *out*, but you can rebind it to any | |
195 PrintWriter. For example, it could be a file opened with | |
196 clojure.java.io/writer. | |
197 | |
198 | |
199 EXTENDING TEST-IS (ADVANCED) | |
200 | |
201 You can extend the behavior of the \"is\" macro by defining new | |
202 methods for the \"assert-expr\" multimethod. These methods are | |
203 called during expansion of the \"is\" macro, so they should return | |
204 quoted forms to be evaluated. | |
205 | |
206 You can plug in your own test-reporting framework by rebinding | |
207 the \"report\" function: (report event) | |
208 | |
209 The 'event' argument is a map. It will always have a :type key, | |
210 whose value will be a keyword signaling the type of event being | |
211 reported. Standard events with :type value of :pass, :fail, and | |
212 :error are called when an assertion passes, fails, and throws an | |
213 exception, respectively. In that case, the event will also have | |
214 the following keys: | |
215 | |
216 :expected The form that was expected to be true | |
217 :actual A form representing what actually occurred | |
218 :message The string message given as an argument to 'is' | |
219 | |
220 The \"testing\" strings will be a list in \"*testing-contexts*\", and | |
221 the vars being tested will be a list in \"*testing-vars*\". | |
222 | |
223 Your \"report\" function should wrap any printing calls in the | |
224 \"with-test-out\" macro, which rebinds *out* to the current value | |
225 of *test-out*. | |
226 | |
227 For additional event types, see the examples in the code. | |
228 "} | |
229 clojure.test | |
230 (:require [clojure.template :as temp] | |
231 [clojure.stacktrace :as stack])) | |
232 | |
233 ;; Nothing is marked "private" here, so you can rebind things to plug | |
234 ;; in your own testing or reporting frameworks. | |
235 | |
236 | |
237 ;;; USER-MODIFIABLE GLOBALS | |
238 | |
239 (defonce | |
240 ^{:doc "True by default. If set to false, no test functions will | |
241 be created by deftest, set-test, or with-test. Use this to omit | |
242 tests when compiling or loading production code." | |
243 :added "1.1"} | |
244 *load-tests* true) | |
245 | |
246 (def | |
247 ^{:doc "The maximum depth of stack traces to print when an Exception | |
248 is thrown during a test. Defaults to nil, which means print the | |
249 complete stack trace." | |
250 :added "1.1"} | |
251 *stack-trace-depth* nil) | |
252 | |
253 | |
254 ;;; GLOBALS USED BY THE REPORTING FUNCTIONS | |
255 | |
256 (def *report-counters* nil) ; bound to a ref of a map in test-ns | |
257 | |
258 (def *initial-report-counters* ; used to initialize *report-counters* | |
259 {:test 0, :pass 0, :fail 0, :error 0}) | |
260 | |
261 (def *testing-vars* (list)) ; bound to hierarchy of vars being tested | |
262 | |
263 (def *testing-contexts* (list)) ; bound to hierarchy of "testing" strings | |
264 | |
265 (def *test-out* *out*) ; PrintWriter for test reporting output | |
266 | |
267 (defmacro with-test-out | |
268 "Runs body with *out* bound to the value of *test-out*." | |
269 {:added "1.1"} | |
270 [& body] | |
271 `(binding [*out* *test-out*] | |
272 ~@body)) | |
273 | |
274 ;;; UTILITIES FOR REPORTING FUNCTIONS | |
275 | |
276 (defn file-position | |
277 "Returns a vector [filename line-number] for the nth call up the | |
278 stack. | |
279 | |
280 Deprecated in 1.2: The information needed for test reporting is | |
281 now on :file and :line keys in the result map." | |
282 {:added "1.1" | |
283 :deprecated "1.2"} | |
284 [n] | |
285 (let [^StackTraceElement s (nth (.getStackTrace (new java.lang.Throwable)) n)] | |
286 [(.getFileName s) (.getLineNumber s)])) | |
287 | |
288 (defn testing-vars-str | |
289 "Returns a string representation of the current test. Renders names | |
290 in *testing-vars* as a list, then the source file and line of | |
291 current assertion." | |
292 {:added "1.1"} | |
293 [m] | |
294 (let [{:keys [file line]} m] | |
295 (str | |
296 ;; Uncomment to include namespace in failure report: | |
297 ;;(ns-name (:ns (meta (first *testing-vars*)))) "/ " | |
298 (reverse (map #(:name (meta %)) *testing-vars*)) | |
299 " (" file ":" line ")"))) | |
300 | |
301 (defn testing-contexts-str | |
302 "Returns a string representation of the current test context. Joins | |
303 strings in *testing-contexts* with spaces." | |
304 {:added "1.1"} | |
305 [] | |
306 (apply str (interpose " " (reverse *testing-contexts*)))) | |
307 | |
308 (defn inc-report-counter | |
309 "Increments the named counter in *report-counters*, a ref to a map. | |
310 Does nothing if *report-counters* is nil." | |
311 {:added "1.1"} | |
312 [name] | |
313 (when *report-counters* | |
314 (dosync (commute *report-counters* assoc name | |
315 (inc (or (*report-counters* name) 0)))))) | |
316 | |
317 ;;; TEST RESULT REPORTING | |
318 | |
319 (defmulti | |
320 ^{:doc "Generic reporting function, may be overridden to plug in | |
321 different report formats (e.g., TAP, JUnit). Assertions such as | |
322 'is' call 'report' to indicate results. The argument given to | |
323 'report' will be a map with a :type key. See the documentation at | |
324 the top of test_is.clj for more information on the types of | |
325 arguments for 'report'." | |
326 :dynamic true | |
327 :added "1.1"} | |
328 report :type) | |
329 | |
330 (defn- file-and-line | |
331 [exception depth] | |
332 (let [^StackTraceElement s (nth (.getStackTrace exception) depth)] | |
333 {:file (.getFileName s) :line (.getLineNumber s)})) | |
334 | |
335 (defn do-report | |
336 "Add file and line information to a test result and call report. | |
337 If you are writing a custom assert-expr method, call this function | |
338 to pass test results to report." | |
339 {:added "1.2"} | |
340 [m] | |
341 (report | |
342 (case | |
343 (:type m) | |
344 :fail (merge (file-and-line (new java.lang.Throwable) 1) m) | |
345 :error (merge (file-and-line (:actual m) 0) m) | |
346 m))) | |
347 | |
348 (defmethod report :default [m] | |
349 (with-test-out (prn m))) | |
350 | |
351 (defmethod report :pass [m] | |
352 (with-test-out (inc-report-counter :pass))) | |
353 | |
354 (defmethod report :fail [m] | |
355 (with-test-out | |
356 (inc-report-counter :fail) | |
357 (println "\nFAIL in" (testing-vars-str m)) | |
358 (when (seq *testing-contexts*) (println (testing-contexts-str))) | |
359 (when-let [message (:message m)] (println message)) | |
360 (println "expected:" (pr-str (:expected m))) | |
361 (println " actual:" (pr-str (:actual m))))) | |
362 | |
363 (defmethod report :error [m] | |
364 (with-test-out | |
365 (inc-report-counter :error) | |
366 (println "\nERROR in" (testing-vars-str m)) | |
367 (when (seq *testing-contexts*) (println (testing-contexts-str))) | |
368 (when-let [message (:message m)] (println message)) | |
369 (println "expected:" (pr-str (:expected m))) | |
370 (print " actual: ") | |
371 (let [actual (:actual m)] | |
372 (if (instance? Throwable actual) | |
373 (stack/print-cause-trace actual *stack-trace-depth*) | |
374 (prn actual))))) | |
375 | |
376 (defmethod report :summary [m] | |
377 (with-test-out | |
378 (println "\nRan" (:test m) "tests containing" | |
379 (+ (:pass m) (:fail m) (:error m)) "assertions.") | |
380 (println (:fail m) "failures," (:error m) "errors."))) | |
381 | |
382 (defmethod report :begin-test-ns [m] | |
383 (with-test-out | |
384 (println "\nTesting" (ns-name (:ns m))))) | |
385 | |
386 ;; Ignore these message types: | |
387 (defmethod report :end-test-ns [m]) | |
388 (defmethod report :begin-test-var [m]) | |
389 (defmethod report :end-test-var [m]) | |
390 | |
391 | |
392 | |
393 ;;; UTILITIES FOR ASSERTIONS | |
394 | |
395 (defn get-possibly-unbound-var | |
396 "Like var-get but returns nil if the var is unbound." | |
397 {:added "1.1"} | |
398 [v] | |
399 (try (var-get v) | |
400 (catch IllegalStateException e | |
401 nil))) | |
402 | |
403 (defn function? | |
404 "Returns true if argument is a function or a symbol that resolves to | |
405 a function (not a macro)." | |
406 {:added "1.1"} | |
407 [x] | |
408 (if (symbol? x) | |
409 (when-let [v (resolve x)] | |
410 (when-let [value (get-possibly-unbound-var v)] | |
411 (and (fn? value) | |
412 (not (:macro (meta v)))))) | |
413 (fn? x))) | |
414 | |
415 (defn assert-predicate | |
416 "Returns generic assertion code for any functional predicate. The | |
417 'expected' argument to 'report' will contains the original form, the | |
418 'actual' argument will contain the form with all its sub-forms | |
419 evaluated. If the predicate returns false, the 'actual' form will | |
420 be wrapped in (not...)." | |
421 {:added "1.1"} | |
422 [msg form] | |
423 (let [args (rest form) | |
424 pred (first form)] | |
425 `(let [values# (list ~@args) | |
426 result# (apply ~pred values#)] | |
427 (if result# | |
428 (do-report {:type :pass, :message ~msg, | |
429 :expected '~form, :actual (cons ~pred values#)}) | |
430 (do-report {:type :fail, :message ~msg, | |
431 :expected '~form, :actual (list '~'not (cons '~pred values#))})) | |
432 result#))) | |
433 | |
434 (defn assert-any | |
435 "Returns generic assertion code for any test, including macros, Java | |
436 method calls, or isolated symbols." | |
437 {:added "1.1"} | |
438 [msg form] | |
439 `(let [value# ~form] | |
440 (if value# | |
441 (do-report {:type :pass, :message ~msg, | |
442 :expected '~form, :actual value#}) | |
443 (do-report {:type :fail, :message ~msg, | |
444 :expected '~form, :actual value#})) | |
445 value#)) | |
446 | |
447 | |
448 | |
449 ;;; ASSERTION METHODS | |
450 | |
451 ;; You don't call these, but you can add methods to extend the 'is' | |
452 ;; macro. These define different kinds of tests, based on the first | |
453 ;; symbol in the test expression. | |
454 | |
455 (defmulti assert-expr | |
456 (fn [msg form] | |
457 (cond | |
458 (nil? form) :always-fail | |
459 (seq? form) (first form) | |
460 :else :default))) | |
461 | |
462 (defmethod assert-expr :always-fail [msg form] | |
463 ;; nil test: always fail | |
464 `(do-report {:type :fail, :message ~msg})) | |
465 | |
466 (defmethod assert-expr :default [msg form] | |
467 (if (and (sequential? form) (function? (first form))) | |
468 (assert-predicate msg form) | |
469 (assert-any msg form))) | |
470 | |
471 (defmethod assert-expr 'instance? [msg form] | |
472 ;; Test if x is an instance of y. | |
473 `(let [klass# ~(nth form 1) | |
474 object# ~(nth form 2)] | |
475 (let [result# (instance? klass# object#)] | |
476 (if result# | |
477 (do-report {:type :pass, :message ~msg, | |
478 :expected '~form, :actual (class object#)}) | |
479 (do-report {:type :fail, :message ~msg, | |
480 :expected '~form, :actual (class object#)})) | |
481 result#))) | |
482 | |
483 (defmethod assert-expr 'thrown? [msg form] | |
484 ;; (is (thrown? c expr)) | |
485 ;; Asserts that evaluating expr throws an exception of class c. | |
486 ;; Returns the exception thrown. | |
487 (let [klass (second form) | |
488 body (nthnext form 2)] | |
489 `(try ~@body | |
490 (do-report {:type :fail, :message ~msg, | |
491 :expected '~form, :actual nil}) | |
492 (catch ~klass e# | |
493 (do-report {:type :pass, :message ~msg, | |
494 :expected '~form, :actual e#}) | |
495 e#)))) | |
496 | |
497 (defmethod assert-expr 'thrown-with-msg? [msg form] | |
498 ;; (is (thrown-with-msg? c re expr)) | |
499 ;; Asserts that evaluating expr throws an exception of class c. | |
500 ;; Also asserts that the message string of the exception matches | |
501 ;; (with re-find) the regular expression re. | |
502 (let [klass (nth form 1) | |
503 re (nth form 2) | |
504 body (nthnext form 3)] | |
505 `(try ~@body | |
506 (do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) | |
507 (catch ~klass e# | |
508 (let [m# (.getMessage e#)] | |
509 (if (re-find ~re m#) | |
510 (do-report {:type :pass, :message ~msg, | |
511 :expected '~form, :actual e#}) | |
512 (do-report {:type :fail, :message ~msg, | |
513 :expected '~form, :actual e#}))) | |
514 e#)))) | |
515 | |
516 | |
517 (defmacro try-expr | |
518 "Used by the 'is' macro to catch unexpected exceptions. | |
519 You don't call this." | |
520 {:added "1.1"} | |
521 [msg form] | |
522 `(try ~(assert-expr msg form) | |
523 (catch Throwable t# | |
524 (do-report {:type :error, :message ~msg, | |
525 :expected '~form, :actual t#})))) | |
526 | |
527 | |
528 | |
529 ;;; ASSERTION MACROS | |
530 | |
531 ;; You use these in your tests. | |
532 | |
533 (defmacro is | |
534 "Generic assertion macro. 'form' is any predicate test. | |
535 'msg' is an optional message to attach to the assertion. | |
536 | |
537 Example: (is (= 4 (+ 2 2)) \"Two plus two should be 4\") | |
538 | |
539 Special forms: | |
540 | |
541 (is (thrown? c body)) checks that an instance of c is thrown from | |
542 body, fails if not; then returns the thing thrown. | |
543 | |
544 (is (thrown-with-msg? c re body)) checks that an instance of c is | |
545 thrown AND that the message on the exception matches (with | |
546 re-find) the regular expression re." | |
547 {:added "1.1"} | |
548 ([form] `(is ~form nil)) | |
549 ([form msg] `(try-expr ~msg ~form))) | |
550 | |
551 (defmacro are | |
552 "Checks multiple assertions with a template expression. | |
553 See clojure.template/do-template for an explanation of | |
554 templates. | |
555 | |
556 Example: (are [x y] (= x y) | |
557 2 (+ 1 1) | |
558 4 (* 2 2)) | |
559 Expands to: | |
560 (do (is (= 2 (+ 1 1))) | |
561 (is (= 4 (* 2 2)))) | |
562 | |
563 Note: This breaks some reporting features, such as line numbers." | |
564 {:added "1.1"} | |
565 [argv expr & args] | |
566 `(temp/do-template ~argv (is ~expr) ~@args)) | |
567 | |
568 (defmacro testing | |
569 "Adds a new string to the list of testing contexts. May be nested, | |
570 but must occur inside a test function (deftest)." | |
571 {:added "1.1"} | |
572 [string & body] | |
573 `(binding [*testing-contexts* (conj *testing-contexts* ~string)] | |
574 ~@body)) | |
575 | |
576 | |
577 | |
578 ;;; DEFINING TESTS | |
579 | |
580 (defmacro with-test | |
581 "Takes any definition form (that returns a Var) as the first argument. | |
582 Remaining body goes in the :test metadata function for that Var. | |
583 | |
584 When *load-tests* is false, only evaluates the definition, ignoring | |
585 the tests." | |
586 {:added "1.1"} | |
587 [definition & body] | |
588 (if *load-tests* | |
589 `(doto ~definition (alter-meta! assoc :test (fn [] ~@body))) | |
590 definition)) | |
591 | |
592 | |
593 (defmacro deftest | |
594 "Defines a test function with no arguments. Test functions may call | |
595 other tests, so tests may be composed. If you compose tests, you | |
596 should also define a function named test-ns-hook; run-tests will | |
597 call test-ns-hook instead of testing all vars. | |
598 | |
599 Note: Actually, the test body goes in the :test metadata on the var, | |
600 and the real function (the value of the var) calls test-var on | |
601 itself. | |
602 | |
603 When *load-tests* is false, deftest is ignored." | |
604 {:added "1.1"} | |
605 [name & body] | |
606 (when *load-tests* | |
607 `(def ~(vary-meta name assoc :test `(fn [] ~@body)) | |
608 (fn [] (test-var (var ~name)))))) | |
609 | |
610 (defmacro deftest- | |
611 "Like deftest but creates a private var." | |
612 {:added "1.1"} | |
613 [name & body] | |
614 (when *load-tests* | |
615 `(def ~(vary-meta name assoc :test `(fn [] ~@body) :private true) | |
616 (fn [] (test-var (var ~name)))))) | |
617 | |
618 | |
619 (defmacro set-test | |
620 "Experimental. | |
621 Sets :test metadata of the named var to a fn with the given body. | |
622 The var must already exist. Does not modify the value of the var. | |
623 | |
624 When *load-tests* is false, set-test is ignored." | |
625 {:added "1.1"} | |
626 [name & body] | |
627 (when *load-tests* | |
628 `(alter-meta! (var ~name) assoc :test (fn [] ~@body)))) | |
629 | |
630 | |
631 | |
632 ;;; DEFINING FIXTURES | |
633 | |
634 (defn- add-ns-meta | |
635 "Adds elements in coll to the current namespace metadata as the | |
636 value of key." | |
637 {:added "1.1"} | |
638 [key coll] | |
639 (alter-meta! *ns* assoc key coll)) | |
640 | |
641 (defmulti use-fixtures | |
642 "Wrap test runs in a fixture function to perform setup and | |
643 teardown. Using a fixture-type of :each wraps every test | |
644 individually, while:once wraps the whole run in a single function." | |
645 {:added "1.1"} | |
646 (fn [fixture-type & args] fixture-type)) | |
647 | |
648 (defmethod use-fixtures :each [fixture-type & args] | |
649 (add-ns-meta ::each-fixtures args)) | |
650 | |
651 (defmethod use-fixtures :once [fixture-type & args] | |
652 (add-ns-meta ::once-fixtures args)) | |
653 | |
654 (defn- default-fixture | |
655 "The default, empty, fixture function. Just calls its argument." | |
656 {:added "1.1"} | |
657 [f] | |
658 (f)) | |
659 | |
660 (defn compose-fixtures | |
661 "Composes two fixture functions, creating a new fixture function | |
662 that combines their behavior." | |
663 {:added "1.1"} | |
664 [f1 f2] | |
665 (fn [g] (f1 (fn [] (f2 g))))) | |
666 | |
667 (defn join-fixtures | |
668 "Composes a collection of fixtures, in order. Always returns a valid | |
669 fixture function, even if the collection is empty." | |
670 {:added "1.1"} | |
671 [fixtures] | |
672 (reduce compose-fixtures default-fixture fixtures)) | |
673 | |
674 | |
675 | |
676 | |
677 ;;; RUNNING TESTS: LOW-LEVEL FUNCTIONS | |
678 | |
679 (defn test-var | |
680 "If v has a function in its :test metadata, calls that function, | |
681 with *testing-vars* bound to (conj *testing-vars* v)." | |
682 {:dynamic true, :added "1.1"} | |
683 [v] | |
684 (when-let [t (:test (meta v))] | |
685 (binding [*testing-vars* (conj *testing-vars* v)] | |
686 (do-report {:type :begin-test-var, :var v}) | |
687 (inc-report-counter :test) | |
688 (try (t) | |
689 (catch Throwable e | |
690 (do-report {:type :error, :message "Uncaught exception, not in assertion." | |
691 :expected nil, :actual e}))) | |
692 (do-report {:type :end-test-var, :var v})))) | |
693 | |
694 (defn test-all-vars | |
695 "Calls test-var on every var interned in the namespace, with fixtures." | |
696 {:added "1.1"} | |
697 [ns] | |
698 (let [once-fixture-fn (join-fixtures (::once-fixtures (meta ns))) | |
699 each-fixture-fn (join-fixtures (::each-fixtures (meta ns)))] | |
700 (once-fixture-fn | |
701 (fn [] | |
702 (doseq [v (vals (ns-interns ns))] | |
703 (when (:test (meta v)) | |
704 (each-fixture-fn (fn [] (test-var v))))))))) | |
705 | |
706 (defn test-ns | |
707 "If the namespace defines a function named test-ns-hook, calls that. | |
708 Otherwise, calls test-all-vars on the namespace. 'ns' is a | |
709 namespace object or a symbol. | |
710 | |
711 Internally binds *report-counters* to a ref initialized to | |
712 *inital-report-counters*. Returns the final, dereferenced state of | |
713 *report-counters*." | |
714 {:added "1.1"} | |
715 [ns] | |
716 (binding [*report-counters* (ref *initial-report-counters*)] | |
717 (let [ns-obj (the-ns ns)] | |
718 (do-report {:type :begin-test-ns, :ns ns-obj}) | |
719 ;; If the namespace has a test-ns-hook function, call that: | |
720 (if-let [v (find-var (symbol (str (ns-name ns-obj)) "test-ns-hook"))] | |
721 ((var-get v)) | |
722 ;; Otherwise, just test every var in the namespace. | |
723 (test-all-vars ns-obj)) | |
724 (do-report {:type :end-test-ns, :ns ns-obj})) | |
725 @*report-counters*)) | |
726 | |
727 | |
728 | |
729 ;;; RUNNING TESTS: HIGH-LEVEL FUNCTIONS | |
730 | |
731 (defn run-tests | |
732 "Runs all tests in the given namespaces; prints results. | |
733 Defaults to current namespace if none given. Returns a map | |
734 summarizing test results." | |
735 {:added "1.1"} | |
736 ([] (run-tests *ns*)) | |
737 ([& namespaces] | |
738 (let [summary (assoc (apply merge-with + (map test-ns namespaces)) | |
739 :type :summary)] | |
740 (do-report summary) | |
741 summary))) | |
742 | |
743 (defn run-all-tests | |
744 "Runs all tests in all namespaces; prints results. | |
745 Optional argument is a regular expression; only namespaces with | |
746 names matching the regular expression (with re-matches) will be | |
747 tested." | |
748 {:added "1.1"} | |
749 ([] (apply run-tests (all-ns))) | |
750 ([re] (apply run-tests (filter #(re-matches re (name (ns-name %))) (all-ns))))) | |
751 | |
752 (defn successful? | |
753 "Returns true if the given test summary indicates all tests | |
754 were successful, false otherwise." | |
755 {:added "1.1"} | |
756 [summary] | |
757 (and (zero? (:fail summary 0)) | |
758 (zero? (:error summary 0)))) |