First of all, add necessary dependency to project.clj
[org.clojure/test.check "0.9.0"]
Then add following line to project.clj :monkeypatch-clojure-test false due to bug
https://github.com/technomancy/leiningen/issues/2173
Then, define fns and their spec
(require '[clojure.spec :as s])
(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?)
Then in test folder make file with tests and put
(:require [clojure.test :as t]
[clojure.string :as str]
[example.core :refer :all]
[clojure.spec :as spec]
[clojure.pprint :as pprint]
[clojure.spec.test :as stest])
(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)))))))
(defspec-test average-test ['example.core/average] {:clojure.spec.test.check/opts {:num-tests 500000}})
Notice that testing fn is fully qualified and ' at the begining in vector.
That's it.
Update:
Also, we can write usual test. To test function example.core/average with input auto generation and output validation with spec we can do it followin way:
(t/deftest average-test2
(t/testing "average test"
(let [r (stest/check `example.core/average {:clojure.spec.test.check/opts {:num-tests 1000}})]
(t/is (= true (-> r first :clojure.spec.test.check/ret :result))))))
Update 2:
To test function example.core/average we need to require [clojure.test.check.clojure-test :as check.clj-test :refer [defspec]]
(defspec average-test3 10 (prop/for-all [r (gen/tuple gen/int gen/int)]
(number? (apply example.core/average r))))
Update 3:
To run reproducible test we need to require [clojure.test.check.clojure-test :as check.clj-test :refer [defspec]]
(defspec abcd {:seed 12345 :num-tests 1000}
(prop/for-all [s (s/gen ::basic/blank-string)]
(s/valid? ::basic/blank-string s)))
Update 4:
In example.core namespace:
(defn my-div [x y]
"divide two numbers"
(/ x y))
(s/fdef my-div
:args (s/and (s/cat :first number? :second number?)
#(not (zero? (:second %))))
:ret number?)
In tests namespace:
(deftest my-div-test
(let [result (spec.test/check `example.core/my-div {:clojure.spec.test.check/opts {:num-tests 1000}})]
(is (= true (-> result first :clojure.spec.test.check/ret :result)))
(clojure.spec.test/instrument 'example.core/my-div)
(is (thrown? clojure.lang.ExceptionInfo (example.core/my-div 1 0)))
(clojure.spec.test/unstrument example.core/my-div)
(is (thrown? ArithmeticException (example.core/my-div 1 0)))))