6

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

1 Answer 1

12

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.