metosin / malli

High-performance data-driven data specification library for Clojure/Script.
Eclipse Public License 2.0
1.51k stars 212 forks source link

Testing utilities #369

Open ikitommi opened 3 years ago

ikitommi commented 3 years ago

common helpers for malli assertions for tests. Can be done in user space, but, might be handy as a ns like malli.test.

From Slack:

aaron51 21 hours ago Yes, it is straightforward enough to write it in user space. But I thought it would be a common case… Found a few similar things: schema: https://github.com/plumatic/schema/blob/master/test/clj/schema/test_macros.clj schema: https://github.com/plumatic/schema/blob/master/test/cljx/schema/core_test.cljx clojure-expectations has “spec expectations” https://github.com/clojure-expectations/clojure-test

borkdude 21 hours ago I also have this one: https://github.com/borkdude/respeced This was written to test fdef specs.

ikitommi commented 3 years ago

relates to #349

apeckham commented 3 years ago

We're using this to write clojure.test assertions like (is (valid [:int] "asdf")). Would you like it as a PR in malli.test?

(defmethod t/assert-expr 'valid
  [msg [_ schema data]]
  `(let [is-valid?# (m/validate ~schema ~data)]
     (t/do-report {:actual ~data
                   :expected (-> ~schema
                                 (m/explain ~data)
                                 (me/humanize))
                   :message ~msg
                   :type (if is-valid?# :pass :fail)})))

Failures look like:

FAIL in () (util_test.clj:14)
expected: ["should be an integer"]
  actual: "asdf"
devurandom commented 1 week ago

Metabase's test runner has something similar:

devurandom commented 6 days ago
(defmethod t/assert-expr 'valid
  [msg [_ schema data]]
  `(let [is-valid?# (m/validate ~schema ~data)]
     (t/do-report {:actual ~data
                   :expected (-> ~schema
                                 (m/explain ~data)
                                 (me/humanize))
                   :message ~msg
                   :type (if is-valid?# :pass :fail)})))

Will this evaluate schema and data three times, once for is-valid?#, once for actual, and again for expected?

I am using this version now:

(defmethod test/assert-expr 'validate
  [msg [_ schema actual]]
  ;; Prevent double-evaluation of `schema` and `actual`:
  `(let [schema# ~schema
         actual# ~actual]
     (test/do-report
      {:type     (if (m/validate schema# actual#) :pass :fail)
       :message  ~msg
       :expected (me/humanize (m/explain schema# actual#))
       :actual   actual#})))