If I have defined the following record:
(defrecord Person [name id])
and the following:
(s/def ::name string?)
(s/def ::id int?)
(s/def ::person (s/keys :req-un [::name ::id]))
How can I ensure that you can't create a Person that does not conform to the ::person spec? In other words, the following should throw an exception:
(->Person "Fred" "3")
I tried:
(s/fdef ->Person :ret ::person)
but calling:
(->Person "Fred" "3")
does not raise an exception.
However:
(s/conform ::person (->Person "Fred" "3"))
does yield the expected:
:clojure.spec/invalid
Thanks
fdef
:ret and :fn specs are only checked during clojure.spec.test/check
tests, but you could use an fdef :args spec to check the inputs to the constructor function when instrumented.
(s/fdef ->Person
:args (s/cat :name ::name :id ::id)
:ret ::person)
(require '[clojure.spec.test :as stest])
(stest/instrument `->Person)
(->Person "Fred" "3")
=> CompilerException clojure.lang.ExceptionInfo: Call to #'spec.examples.guide/->Person did not conform to spec:
In: [1] val: "3" fails spec: :spec.examples.guide/id at: [:args :id] predicate: int?
:clojure.spec/args ("Fred" "3")
:clojure.spec/failure :instrument
:clojure.spec.test/caller {:file "guide.clj", :line 709, :var-scope spec.examples.guide/eval3771}
It wouldn't be too hard to macro the combination of defrecord and fdef of the constructor using the matching specs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With