8

I have this (not (some #(= (:length %1) 0) %)) as a postcondition. Written like this it's pretty clear, but when this condition isn't met I get this:

Assert failed: (not (some (fn* [p1__17852#] (= (:length p1__17852#) 0)) %))

Which isn't very readable. Is there a way to define the message for a postcondition, or for a precondition?

Edit 1:

Following noahlz and noisesmiths suggestion, (but using an external named function):

(defn not-zero-length
  [evseq]
  (not (some (fn [item] (= (:length item) 0)) evseq)))

(defn my-func
  [evseq]
  {:post [(not-zero-length %)]}
  evseq)

(my-func '({:length 3}{:length 0}))

gives:

AssertionError Assert failed: (not-zero-length %)

Which is alot clearer.

snowape
  • 1,274
  • 10
  • 23
  • This answer solved this problem for me https://stackoverflow.com/a/24874961/3646180 – Jp_ Aug 04 '18 at 15:02

3 Answers3

7

This is discussed in the following clojure mailing list thread.

Looking at the clojure.core source you can see the fn macro only passes in a boolean to the assert function, and does not include an optional parameter for passing an additional message argument in.

So it looks like there is no way to do this cleanly yet.

O.Powell
  • 172
  • 2
  • 8
  • That thread is two and a half years old, and wasn't commented on by anyone with any decision-making power. I would not say that this feature is being considered for inclusion. – amalloy Jun 30 '13 at 22:47
  • Seems like the solution is to not use more descriptive function names in your asserts (not inline functions). – noahlz Jul 01 '13 at 00:13
  • Updated answer to reflect what amalloy noted. – O.Powell Jul 01 '13 at 10:38
6

This post in the same thread suggests the use of the clojure.test/is macro, which returns a meaningful error message.

(require '[clojure.test :refer [is]])

(defn get-key [m k]
  {:pre [(is (map? m) "m is not a map!")]}
  (m k))

(get-key [] 0)

returns

FAIL in clojure.lang.PersistentList$EmptyList@1 (form-init8401797809408331100.clj:2)
m is not a map!
expected: (map? m)
  actual: (not (map? []))
AssertionError Assert failed: (is (map? m) "m is not a map!")  
shark8me
  • 638
  • 8
  • 9
  • 1
    Usage of clojure.test/is outside of tests will mess up your testing stats. Eg. when you run your tests, it will report way higher "number of assertions" than what is really happening in the test. Therefore I'd not recommend this approach. – Istvan Devai Apr 19 '21 at 19:29
2

expanding on a suggestion above:

(not (some (fn zero-length [item] (= (:length item) 0)) %))

when you name an anonymous function, any errors involving that fn will be more readable

also, how is it that you have two % substitutions above? #() does not nest.

noisesmith
  • 20,076
  • 2
  • 41
  • 49
  • % in a postcondition gives the return value of the function. Another reason to use `(fn ...)` instead of `#(...)` I guess. – snowape Jul 01 '13 at 08:15