Reputation: 1681
Let's say you have a ::givee
and a ::giver
:
(s/def ::givee keyword?)
(s/def ::giver keyword?)
That form a unq/gift-pair
:
(s/def :unq/gift-pair (s/keys :req-un [::givee ::giver]))
And then you have a :unq/gift-history
which is a vector
of unq/gift-pair
:
(s/def :unq/gift-history (s/coll-of :unq/gift-pair :kind vector?))
Last, suppose you want to replace one of the :unq/gift-pair
in the vector
:
(defn set-gift-pair-in-gift-history [g-hist g-year g-pair]
(assoc g-hist g-year g-pair))
(s/fdef set-gift-pair-in-gift-history
:args (s/and (s/cat :g-hist :unq/gift-history
:g-year int?
:g-pair :unq/gift-pair)
#(< (:g-year %) (count (:g-hist %)))
#(> (:g-year %) -1))
:ret :unq/gift-history)
All works fine:
(s/conform :unq/gift-history
(set-gift-pair-in-gift-history [{:givee :me, :giver :you} {:givee :him, :giver :her}] 1 {:givee :dog, :giver :cat}))
=> [{:givee :me, :giver :you} {:givee :dog, :giver :cat}]
Until I try tostest/check
it:
(stest/check `set-gift-pair-in-gift-history)
clojure.lang.ExceptionInfo: Couldn't satisfy such-that predicate after 100 tries.
java.util.concurrent.ExecutionException: clojure.lang.ExceptionInfo: Couldn't satisfy such-that predicate after 100 tries. {}
I have tried using s/int-in
to limit the vector count (thinking that may be the problem) without success.
Any ideas on how to run (stest/check `set-gift-pair-in-gift-history)
correctly?
Thank you.
Upvotes: 4
Views: 947
Reputation: 16194
The problem is that the generators for the vector and the index into the collection are independent/unrelated. The random vectors and integers aren't satisfying these criteria:
#(< (:g-year %) (count (:g-hist %)))
#(> (:g-year %) -1)
To check
this function you can supply a custom generator that will generate the random :unq/gift-history
vector, and build another generator for the index based on the size of that vector:
(s/fdef set-gift-pair-in-gift-history
:args (s/with-gen
(s/and
(s/cat :g-hist :unq/gift-history
:g-year int?
:g-pair :unq/gift-pair)
#(< (:g-year %) (count (:g-hist %)))
#(> (:g-year %) -1))
#(gen/let [hist (s/gen :unq/gift-history)
year (gen/large-integer* {:min 0 :max (max 0 (dec (count hist)))})
pair (s/gen :unq/gift-pair)]
[hist year pair]))
:ret :unq/gift-history)
This is using test.check's let
macro, which is a convenience over bind
/fmap
that allows you to combine/compose generators using code that looks like a regular let
. The custom generator returns a vector of arguments to the function.
Upvotes: 6