Property-Based Testing
When a property fails, test.check shrinks the input to a minimal failing case. Combine with clojure.spec (stest/check) to test functions directly from their specs.
(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop]
'[clojure.test.check.clojure-test :refer [defspec]])
;; Properties: claims that should hold for ALL generated inputs
(def reverse-twice-identity
(prop/for-all [v (gen/vector gen/small-integer)]
(= v (reverse (reverse v)))))
(tc/quick-check 100 reverse-twice-identity) ; {:result true, :num-tests 100, ...}
(defspec sort-is-idempotent 200
(prop/for-all [v (gen/vector gen/small-integer)]
(= (sort v) (sort (sort v)))))
(defspec sort-preserves-elements 200
(prop/for-all [v (gen/vector gen/small-integer)]
(= (frequencies v) (frequencies (sort v)))))
;; Generators
(gen/sample gen/string-alphanumeric 5)
(gen/sample (gen/choose 1 6) 10)
(gen/sample (gen/elements [:red :green :blue]))
(def user-gen (gen/hash-map :name gen/string-alphanumeric :age (gen/choose 0 120)))
(gen/sample user-gen 3)
(gen/sample (gen/fmap #(* 2 %) gen/nat)) ; even numbers
(gen/sample (gen/such-that not-empty (gen/vector gen/nat)))