Reputation: 119
Given that :post
takes a form that gets evaluated later (e.g. {:post [(= 10 %)]}
). How could one dynamically pass a 'pre-made' vector of functions to :post
?
For example:
(def my-post-validator
[prediate1 predicate2 predicate3])
(defn foo [x]
{:post my-post-validator}
x)
this throws a syntax error
Don't know how to create ISeq from: clojure.lang.Symbol
With my fuzzy understanding, it's because defn is a macro, and the thing that allows the %
syntax in :post
is that it's quoted internally..?
I thought maybe I then use a macro to pass a 'literal' of what I wanted evaluated
(defmacro my-post-cond [spec]
'[(assert spec %) (predicate2 %) (predicate n)])
example:
(defn foo [x]
{:post (my-post-cond :what/ever)}
x)
However, this attempt gives the error:
Can't take value of a macro
Is there a way to pass a vector of things to :post
rather than having to define it inline?
Upvotes: 0
Views: 66
Reputation: 29976
I started off as a fan of pre-
and post-conditions
, but I've changed over the years.
For simple things, I prefer to use Plumatic Schema to not only test inputs & outputs, but to document them as well.
For more complicated tests & verifications, I just put in an explicit assert
or similar. I also wrote a helper function in the Tupelo library to reduce repetition, etc when debugging or verifying return values:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn oddly
"Transforms its input. Throws if result is not odd"
[x]
(let [answer (-> x (* 3) (+ 2))]
(with-result answer
(newline)
(println :given x)
(assert (odd? answer))
(println :returning answer))))
(dotest
(is= 5 (oddly 1))
(throws? (oddly 2)))
with result
------------------------------------
Clojure 1.10.3 Java 11.0.11
------------------------------------
Testing tst.demo.core
:given 1
:returning 5
:given 2
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
Passed all tests
So with either the println
or assert
, the returned value is easy to see. If it fails the assert
, an Exception is thrown as normal.
Upvotes: 0
Reputation: 10727
You can't pass a vector of predefined predicates, but you can combine multiple predicates under a single name and use that name in :post
:
(defn my-post-cond [spec val]
(and
;; Not sure if this is exactly what you want,
;; given that `val` becomes an assert message.
(assert spec val)
(predicate2 val)
;; You used `n` - I assume it was supposed to be `%`.
(predicate val)))
(defn foo [x]
{:post [(my-post-cond :what/ever %)]}
x)
Upvotes: 1