(ns core.demo (:require [clojure.test.check :as tc] [clojure.spec.alpha :as spec] [clojure.spec.test.alpha :as stest] [clojure.test.check.properties :as prop] [clojure.test.check.generators :as gen])) ;; We can define new specs with spec/def often aliased as s/def, this takes a namespace's keyword and a spec ;; there are a lot of built in specs like int? pos-int? string? etc you can also use sets (spec/def ::id pos-int?) (spec/def ::username string?) (spec/def ::age (spec/int-in 0 100)) ;; example namespaced version the above is namespaced to core.demo, this is a set of allowed characters (spec/def :core/hex-digit #{"0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F"}) ;; Check if a value conforms to a spec (spec/valid? ::id "ABC-123") (spec/valid? ::id 4) (spec/valid? ::age 99) (spec/valid? ::age 101) ;; we can create more custom specs like a html hex colour code, there is no built in so we ;; need a custom generator in this situation, we create a function that can generate the data (spec/def ::demo (spec/spec string? :gen (fn [] (gen/return "123")))) ;; usually nicer to make a custom function, the blow fn create a vector of 6 hex characters ;; converts it into a string and append # to the beginning (defn gen-hex-colour [] (gen/fmap #(str "#" (apply str %)) (gen/vector (spec/gen :core/hex-digit) 6))) ;; define the colour spec it has to pass the regex below, string? would never generate the correct Combination ;; so we supply a way of generating the data (spec/def :core/hex-colour (spec/spec (spec/and string? #(re-matches #"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" %)) :gen gen-hex-colour)) ;; we can also spec out hash maps, this is very useful when dealing with json api's ;; we can fake a response data set but also validate the real response to catch changes at the edges of our code (spec/def ::user (spec/keys :req-un [::id ::username] :opt-un [::age])) ;; We can also tests functions by defining the inputs and outputs to the functions and using gen/exercise to repeatedly send data. ;; here we are making sure an integer goes in and an integer always comes out ;; if nil or some other type is returned stest/check will fail (spec/fdef demo-function :args (spec/cat :value int?) :ret int?) (defn demo-function [value] (+ 5 value)) (stest/instrument `demo-function) (stest/check `demo-function) ;; to generate a single value we use generate we can also use sample to get a list of values (gen/generate (spec/gen ::id)) (gen/generate (spec/gen ::age)) (gen/generate (spec/gen ::user)) (gen/sample (spec/gen ::user)) (gen/sample (spec/gen ::id)) (gen/sample (spec/gen ::username)) (gen/sample (spec/gen ::age)) (gen/sample (spec/gen :core/hex-colour)) ;; Bonus simple spec that you can use to insert dummy data into the demo datascript example (spec/def :user/name string?) (spec/def :room/name string?) (spec/def :room/link string?) (spec/def :room/description string?) (spec/def :datalog/room (spec/keys :req [:room/name] :opt [:room/link :room/description])) (spec/def :user/rooms (spec/coll-of :datalog/room :kind vector?)) (spec/def :datalog/user (spec/keys :req [:user/name] :opt [:user/rooms])) (gen/sample (spec/gen :datalog/user)) (def datalog-data (gen/sample (spec/gen :datalog/user))) ;; we can just generate a sample straight into the datalog transact function like below ;;(d/transact! conn (gen/sample (spec/gen :datalog/user)))