2016-11-19 103 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)))) 
相关问题