Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Spec by
Custom generators may build (via fmap/bind) on spec generators. Generator overrides at the top level will not take effect inside custom generators:


(require '[clojure.spec :as s])
(require '[clojure.test.check.generators :as gen])

;; A map that holds a single integer value
(s/def ::val integer?)
(s/def ::body (s/keys :req [::val]))

;; This spec matches stringified versions of 'body'.
;; (read-string is for demonstration purposes only, of course)
(s/def ::stringy-body
  (s/with-gen
    (s/and string? #(s/valid? ::body (read-string %)))
    #(gen/fmap pr-str (s/gen ::body))))

(s/valid? ::stringy-body "{:user/val 37}") ;; => true

;; This makes various stringified maps, as expected
(take 3 (gen/sample (s/gen ::stringy-body)))
;; => ("#:user{:val -1}" "#:user{:val 0}" "#:user{:val -1}")

;; *** But the overrides don't get passed through ***
(take 3 (gen/sample (s/gen ::stringy-body {::val #(s/gen #{42})})))
;; ("#:user{:val -1}" "#:user{:val 0}" "#:user{:val 0}")


Should consider documenting this in s/gen, s/with-gen, etc.

5 Answers

0 votes
by

Comment made by: alexmiller

When you use with-gen, you're basically overriding the built-in gen mechanism (which supports overrides) and providing your own (opaque to spec) generator. You should not expect overrides to take effect inside a custom generator.

0 votes
by

Comment made by: mullr

That makes sense, but in lieu of that I expected (and went looking for) some way to get at the overrides map from the function passed to s/with-gen, and found none.

0 votes
by

Comment made by: mullr

... I didn't fully parse your comment the first time around. I can see from the implementation that a custom generator (gfn internally) is never passed any of the contextual information that the builtin specs have at hand. As it sounds like this is intentional, it would be useful to note this limitation in the docstring for s/gen or perhaps s/with-gen.

0 votes
by

Comment made by: alexmiller

It's not a crazy idea, but it doesn't seem like there's any way this could be done in the current impl without some pretty significant changes.

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2095 (reported by alex+import)
...