2016-04-11 17 views
2

私はClojureを1ヶ月前に使い始めました。私の統合テストでは、関数が他の関数から呼び出されたかどうかを調べるための小さなマクロを開発しました。マクロは、現在、次のようになります。関数が呼び出されたかどうかを確認するClojureマクロ

(defmacro called? 
    [f val body] 
    (let [flag (gensym `flag)] 
    `(with-redefs-fn 
     {~f (fn [& rest#] (def ~flag nil) ~val)} 
     #(do 
     (try 
      (~body) 
      (catch Throwable e#)) 
     (if (resolve '~flag) 
      (bound? (resolve '~flag)) 
      false))))) 

使用法:

user=> (called? #'clojure.core/println nil #(println "hey")) 
true 
user=> (called? #'clojure.core/println nil #(print "hey")) 
heyfalse 

スタックを爆発させずに元の実装を呼び出す方法はありますか?私はあなたの目標は、このようなマクロを自分で書くこと、または既存のライブラリを使用することができているかどうかわからないです私は最初letにこのような何かをしようとしたが、これはClassCastException clojure.lang.Cons cannot be cast to java.util.concurrent.Future

(let [flag (gensym `flag) 
      orig-f# (deref f)] ...) 

答えて

0

で終わります。

私だけではなく"spy" calls to a set of functionsにあなたを可能にconjureをお勧めしますが、また、これらの関数に渡されたものを引数を検証します:

(defn some-fn [n] 
    (inc n)) 

(deftest test-instumenting 
    (instrumenting [some-fn] 
       (is (= 43 (some-fn 42))) 
       (verify-called-once-with-args some-fn 42))) 

カンジャーもモックや外部リソースからテストを分離するためのスタブをサポートしています。

あなたが自分でそれを実装したいのであれば、私はいくつかの良いプラクティスを促進する、呪文の実装に従います。

あなたのマクロは、主な作業を行う機能を使用するシンタックスシンタックスレイヤーでなければなりません。元のものが呼び出された場合に記録する折り返し機能を作成します。折り返し機能を使用してカウントを別々に保存することで、後で検査することができるため、元の機能をそのまま使用して実際の結果を返すことができます。あなたのユーティリティ機能のセットアップを

(def call-counts (atom {})) 

(defn instrumented-fn [original-fn] 
    (let [wrapping-fn (fn this [& args] 
         (swap! call-counts 
          update-in 
          [original-fn] 
          inc) 
         (apply original-fn args))] 
    (swap! call-counts assoc original-fn 0) 
    wrapping-fn)) 

    (defn have-been-called? 
     [original-fn] 
     (pos? (@call-counts original-fn 0)) 

    (defn reset-counters! [] (reset! call-counts {})) 

、あなたはマクロで薄いシンタックスシュガー層を追加することができます。

(defmacro instrumenting [f & body] 
    `(with-redefs [~f (instrumented-fn ~f)] 
    [email protected])) 

(defmacro called? [f & body] 
    `(let [origin-fn# ~f] 
    (instrumenting ~f [email protected]) 
    (have-been-called? origin-fn#))) 

使用法:

(defn my-fn [n] (inc n)) 

(have-been-called? my-fn) 
;; => false 

(instrumenting my-fn (dec 1)) 
;; => 0 

(have-been-called? my-fn) 
;; => false 

(instrumenting my-fn (my-fn 1)) 
;; => 2 

(have-been-called? my-fn) 
;; => true 

(reset-counters!) 
(have-been-called? my-fn) 
;; => false 

(called? my-fn (dec 1)) 
;; => false 

(called? my-fn (my-fn 1)) 
;; => true 
関連する問題