2016-11-19 38 views
6

是否有無法在通用測試套件中包含clojure.spec'd函數?我知道我們可以register specs和直接spec functions如何在測試套件中包含clojure.spec'd函數

(ns foo 
    (:require [clojure.spec :as s] 
      [clojure.spec.test :as stest])) 

(defn average [list-sum list-count] 
    (/ list-sum list-count)) 

(s/fdef average 
     :args (s/and (s/cat :list-sum float? :list-count integer?) 
        #(not (zero? (:list-count %)))) 
     :ret number?) 

後來,如果我想運行鍼對該功能只具備生成測試,我可以使用stest/check

=> (stest/check `average) 
({:speC#object[clojure.spec$fspec_impl$reify__14282 0x68e9f37c "[email protected]"], :clojure.spec.test.check/ret {:result true, :num-tests 1000, :seed 1479587517232}, :sym edgar.core.analysis.lagging/average}) 

我)反正是有包括這些測試中我一般測試套件運行?我正在考慮test.check has的那種clojure.test集成。最近的東西,我可以看到ii)stest/instrument(見here)的功能。但是,這似乎讓我們開始檢查repl。不完全是我想要的。另外,iii)是否已註冊功能規格?

(defspec foo-test 
     100 

     ;; NOT this 
     #_(prop/for-all [v ...] 
      (= v ...)) 

     ;; but THIS 
     (stest/some-unknown-spec-fn foo)) 

回答

7

好的,解決了這個問題。結果表明沒有開箱即用的解決方案。但是clojure-spec懈怠頻道上的一些人已經爲clojure.spec.testclojure.test制定了defspec-test解決方案。

所以給了問題中的代碼。您可以A)定義defspec-test宏,其中包含您的測試名稱和特定功能列表。然後你可以使用B)在你的測試套件中使用它。

感謝Clojure社區!並希望這樣的實用功能使其成爲核心庫。

A)

(ns foo.test 
    (:require [clojure.test :as t] 
      [clojure.string :as str])) 

(defmacro defspec-test 
    ([name sym-or-syms] `(defspec-test ~name ~sym-or-syms nil)) 
    ([name sym-or-syms opts] 
    (when t/*load-tests* 
    `(def ~(vary-meta name assoc 
         :test `(fn [] 
           (let [check-results# (clojure.spec.test/check ~sym-or-syms ~opts) 
             checks-passed?# (every? nil? (map :failure check-results#))] 
            (if checks-passed?# 
            (t/do-report {:type :pass 
                :message (str "Generative tests pass for " 
                   (str/join ", " (map :sym check-results#)))}) 
            (doseq [failed-check# (filter :failure check-results#) 
              :let [r# (clojure.spec.test/abbrev-result failed-check#) 
                failure# (:failure r#)]] 
             (t/do-report 
             {:type  :fail 
             :message (with-out-str (clojure.spec/explain-out failure#)) 
             :expected (->> r# :spec rest (apply hash-map) :ret) 
             :actual (if (instance? Throwable failure#) 
                failure# 
                (:clojure.spec.test/val failure#))}))) 
            checks-passed?#))) 
     (fn [] (t/test-var (var ~name))))))) 

B)

(ns foo-test 
    (:require [foo.test :refer [defspec-test]] 
      [foo])) 


(defspec-test test-average [foo/average]) 
+0

我用'clojure.spec.alpha'替換了調用'clojure.spec',並且使用了Clojure 1.9。代碼沒有發現錯誤...你有更新的版本嗎? – DjebbZ

1

上面example可以在失敗的情況下,其中:failurefalse由於如何stest/abbrev-result試驗失敗。有關更多詳細信息,請參閱CLJ-2246。你可以通過定義你自己的版本abbrev-result來解決這個問題。此外,故障數據的格式已更改。

(require 
'[clojure.string :as str] 
'[clojure.test :as test] 
'[clojure.spec.alpha :as s] 
'[clojure.spec.test.alpha :as stest]) 

;; extracted from clojure.spec.test.alpha 
(defn failure-type [x] (::s/failure (ex-data x))) 
(defn unwrap-failure [x] (if (failure-type x) (ex-data x) x)) 
(defn failure? [{:keys [:failure]}] (not (or (true? failure) (nil? failure)))) 

;; modified from clojure.spec.test.alpha 
(defn abbrev-result [x] 
    (let [failure (:failure x)] 
    (if (failure? x) 
     (-> (dissoc x ::stc/ret) 
      (update :spec s/describe) 
      (update :failure unwrap-failure)) 
     (dissoc x :spec ::stc/ret)))) 

(defn throwable? [x] 
    (instance? Throwable x)) 

(defn failure-report [failure] 
    (let [expected (->> (abbrev-result failure) :spec rest (apply hash-map) :ret)] 
    (if (throwable? failure) 
     {:type :error 
     :message "Exception thrown in check" 
     :expected expected 
     :actual failure} 
     (let [data (ex-data (get-in failure 
            [::stc/ret 
            :result-data 
            :clojure.test.check.properties/error]))] 
     {:type  :fail 
     :message (with-out-str (s/explain-out data)) 
     :expected expected 
     :actual (::s/value data)})))) 

(defn check? 
    [msg [_ body :as form]] 
    `(let [results# ~body 
     failures# (filter failure? results#)] 
    (if (empty? failures#) 
     [{:type :pass 
     :message (str "Generative tests pass for " 
         (str/join ", " (map :sym results#)))}] 
     (map failure-report failures#)))) 

(defmethod test/assert-expr 'check? 
    [msg form] 
    `(dorun (map test/do-report ~(check? msg form)))) 
相關問題