I use clojure.spec to verify input data. and I test the spec using Expectations as following: (latest version: https://gitlab.com/snippets/1638290)

(ns expectations.ext
  (:require #?(:cljs [cljs.js :as cljs])
            #?(:clj  [clojure.spec :as s]
               :cljs [cljs.spec :as s])
            [clojure.string :as str]
            [expectations :refer [CustomPred]]))

   (defn eval
     (let [ret   (atom nil)
           state (cljs/empty-state)
           opt   {:eval cljs/js-eval :context :expr}]
       (cljs/eval state form opt #(reset! ret (:value %)))

(defrecord SpecPred [ret-fn spec]
  (expect-fn [_ arg]
    (ret-fn (s/valid? spec arg)))

  (expected-message [_ arg _ _])
  (actual-message   [_ arg _ _])
  (message [_ arg _ _]
    (->> (for [edata (::s/problems (s/explain-data spec arg))]
           (let [pred    (:pred edata)
                 (cond-> pred
                   (seq? pred)
                   (->> (list)
                        (concat (->> pred
                                     (filter #(and (symbol? %)
                                                   (str/starts-with? (name %) "%")))
                                     (list 'fn)))
                 val (:val edata)]
             (str "Invalid spec: " (:via edata) ", "
                  "(" (cond->> (pr-str pred) (seq? pred) (str "#"))
                  " " (pr-str val) ")"
                  " => " (pr-str (pred-fn val)))))
         (interpose "\n\n           ")
         (apply str))))

(defn spec
  (->SpecPred identity spec))

(defn spec:failure
  (->SpecPred complement spec))

<2017-03-02 Thu>: udpate.