I've been working on a small library to assert that functions are
called during a unit test.

The syntax looks like:

(calls [[user/foo :times 2 :returns 3]
           [user/bar :returns 42]]
    (fn-being-tested))

This will assert that foo is called exactly twice, and bar is called
exactly once inside the block. I plan to add support for asserting
that the functions are called with the right arguments.

Comments appreciated, especially about my use of macros, and the
syntax.


(ns clojure.contrib.expectation
  (:require [clojure.contrib.test-is :as test]))

(defstruct expectation-hash :called :failed :times)

(defn expect-obj [[var & args]]
  (let [opts (apply hash-map args)
        state (ref (struct-map expectation-hash :name var :called
0 :failed [] :times (get args :times 1)))]
    {:fn (fn [& _]
           (dosync
            (alter state assoc :called (inc (@state :called))))
           (opts :returns))
     :state state}))

(defn- validate-expectation
  "checks that the expectation was met. Returns true on success"
  [expt]
  (let [expected-calls (@(expt :state) :times)
        actual-calls (@(expt :state) :called)]
    (if (= expected-calls actual-calls)
      (do
        (test/is true (str (@(expt :state) :name) "was called correctly"))
        true)
      (test/failure (@(expt :state) :name) (str " expected " expected-
calls " actually called " actual-calls " times")))))

(defmacro calls [args & body]
  "creates a stub function and asserts the fn was called the correct
number of times

   (calls [[user/foo :times 2 :returns 42]
           [user/bar :times 1 :returns 3]]
         (user/foo x y z)
         (user/bar z)"

  (let [quote-name (fn [sym] `(var ~sym))
        get-name (fn [expt] (first expt))
        names (into [] (map (comp quote-name get-name) args))]
    `(let [expect-opts# (map rest ~args)
           expectations# (map (fn [name# arglist#] (expect-obj (cons name#
arglist#))) ~names expect-opts#)
           fns# (map :fn expectations#)
           thread-bindings# (apply hash-map (interleave ~names fns#))]
       (. clojure.lang.Var (pushThreadBindings thread-bindings#))
       (try
        [EMAIL PROTECTED]
        (finally
         (. clojure.lang.Var (popThreadBindings))))
       (every? validate-expectation expectations#))))
--~--~---------~--~----~------------~-------~--~----~
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
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to