More precise output from clojure.spec/valid? in a test?


Case: I have json files in my resources. I change them a lot, so I want to check them as part of a unit test.

Current solution use s/valid? in the test. I’d like to see more precise error messages.

;; dogdb.clj
(ns sample.dogdb
   [clojure.spec.alpha :as s]))

(s/def ::name string?)
(s/def ::birthyear int?)

(s/def ::dog
   [::name ::birthyear]))

(s/def ::dogs
  (s/coll-of ::dog))

(s/def ::dogdb
;; dogdb_test.clj
(ns sample.dogdb-test
   [ :as json]
   [ :as io]
   [clojure.spec.alpha :as s]
   [clojure.test :refer :all]
   [clojure.walk :as walk]

   [sample.dogdb :as dogdb]

(defn load-json-resource [resource-path]
  (-> (io/resource resource-path)

(deftest dogdb-resource
  (testing "resources/dogdb.json is valid dogdb"
    (is (s/valid? ::dogdb/dogdb
                  (load-json-resource "resources/dogdb.json")))))

When I run the unit test with kaocha, I get output like

FAIL in sample.dogdb-test/dogdb-resource (dogdb_test.clj:21)
resources/dogdb.json is valid dogdb
expected: (s/valid? ::dogdb/dogdb
                  (load-json-resource "resources/dogdb.json"))
  actual: (not (s/valid? ::dogdb/dogdb
                  (load-json-resource "resources/dogdb.json")))

I’d like to see what was actual wrong. Was a dog missing name? Was there a cat in the dog list? Was there no :dogs entry top level?

Could I use something else than clojure.test/is? Recommendations much welcome!


You can probably use s/explain-data for this.

(t/deftest spec-test
  (t/is (not (::s/problems (s/explain-data int? "s")))))


Testing user

FAIL in (spec-test) (NO_SOURCE_FILE:2)
expected: (not (:clojure.spec.alpha/problems (s/explain-data int? "s")))
  actual: (not (not [{:path [], :pred clojure.core/int?, :val "s", :via [], :in []}]))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
{:test 1, :pass 0, :fail 1, :error 0, :type :summary}
1 Like

Right, so I won’t need to replace t/is because when I use s/explain-data, I get a description of what’s actually wrong.


This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.