I used the last days to dig deeper into clojure.spec in Clojure and ClojureScript.
Until now I find it most useful, to use specs as guards in :pre
Without taking into account if you should use pre and post conditions to validate function arguments, there is a way to print somewhat clearer messages from pre and post conditions by wrapping your predicate with clojure.test/is
, as suggested in the answer below:
How can I get Clojure :pre & :post to report their failing value?
So then your code could look like this:
(ns pre-post-messages.core
(:require [clojure.spec :as s]
[clojure.test :as t]))
(defn person-name [person]
{:pre [(t/is (s/valid? ::person person))]
:post [(t/is (s/valid? string? %))]}
(str (::first-name person) " " (::last-name person)))
(def try-1
{:first-name "Anna Vissi"})
(def try-2
{::first-name "Anna"
::last-name "Vissi"
::email "Anna@Vissi.com"})
(s/def ::person (s/keys :req [::first-name ::last-name ::email]))
Evaluating
pre-post-messages.core> (person-name try-2)
would produce
"Anna Vissi"
and evaluating
pre-post-messages.core> (person-name try-1)
would produce
FAIL in () (core.clj:6)
expected: (s/valid? :pre-post-messages.core/person person)
actual: (not (s/valid? :pre-post-messages.core/person {:first-name "Anna Vissi"}))
AssertionError Assert failed: (t/is (s/valid? :pre-post-messages.core/person person)) pre-post-messages.core/person-name (core.clj:5)