If the local bindings will never change, then why not just use (binding [whatever-setup ...] ...) wrapping the individual test bodies that need such setup? (Where explicit tear-down is required, you'd need try ... finally as well, or better yet a macro like with-open, but using binding instead of let, to abstract out the repeated aspects of such code. Common setups could become more macros, e.g. (with-foo-whatsit [some-symbol some-other-symbol] ...).)
If the bindings are to stateful stuff that a sequence of tests will alter, though, you have more problems. Possible improvements: * Redesign the whole thing to be more functional and less stateful. * At least, move as much into pure functions as possible; the pure functions are easy to test. * The (remaining) sequences of tests on state will have to run sequentially, so combine them into a single test that calls the smaller ones. You'll end up with something like: (def results (atom {})) (def tests (atom [])) (def foo nil) (def bar nil) (defmacro deftest ... ) (deftest foo nil [foo (initialize-some-resource) bar (initialize-another)] (the test goes here))) and that produces something like (do (defn foo [] (binding [foo (initialize-some-resource)] (try (binding [bar (initialize-another)] (try (deliver (@results foo) (the test goes here)) (finally (.close bar)))) (catch Throwable _ (deliver (@results foo) false)) (finally (.close foo))))) (swap! results assoc foo (promise)) (swap! tests conj foo)) whereas (deftest bar foo ...) is similar, but with foo instead of nil for the second argument the bar function body gets wrapped in: (if @(@results foo) (do (body that would have been) (with nil second arg)) (deliver (@results bar) false)) so when bar is run it blocks until foo has run, and short-circuits to failing if foo failed, but runs if foo succeeded. And then there'd be (deftest baz :subtest ...) which expands without swap!s or a wrapper -- it just becomes a function like foo and nothing else. A later test body can do setup, call baz as a function along with several other subtests, and do teardown, e.g. (deftest quux ... ... (and (baz) (baz2) (baz3))) which, obviously, short-circuits to failure if any of these fails and otherwise runs them all and returns baz3's result. Lastly, you'd have (doall (apply pcalls @tests)) to run the tests and (report) after that, with (defn report (doseq [[test result] @results] (println (:name (meta test)) " - " @result))) or whatever. If you want more detailed reports, deftest could put additional stuff into the metadata of the functions (and have additional arguments, if necessary). The above framework seems like it would do what you want: allow tests to have setup and teardown and simplify that, make the stuff thus set up be dynamic bindings visible to subsidiary functions, allow tests to depend on earlier tests and block until the earlier tests complete, and allow a sequence of tests to be run within a single set-up environment run sequentially, in a single thread, in that environment. The major downside is that pcalls is optimized for cpu-bound jobs; if the tests are I/O-bound you'll want to change pcalls to something based around Executor instead, with a thread pool however large you want. The other is that tests that must be run in the same environment, with just one setup before all the tests and one teardown after, will be a single unit as far as parallelism goes and will stop on the first failed test. The framework can be modified to let subtests create separate result entries, though, and using (let [a (baz) b (baz2) c (baz3)] (and a b c)) will let you run more subtests even if one fails, when you know the setup environment won't have been borked by the failures. Also, tests that should get an exception require you to explicitly catch the exception exception and return true, e.g. (try (this-should-throw-something) false (catch FooException _ true)). You could add a bunch of (expect ...) macros to simplify further the writing of tests. If you're trying to use an existing testing framework like midje, though, you're probably SOL unless the framework developer provides their own parallel testing support or you can understand the framework code well enough to marry it to something like the above. -- Protege: What is this seething mass of parentheses?! Master: Your father's Lisp REPL. This is the language of a true hacker. Not as clumsy or random as C++; a language for a more civilized age. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en