Fork me on GitHub
#clojure-spec
<
2019-07-08
>
Stefan08:07:13

Hi, I’d like to write a spec for an entry in a map that must always be a specific string, and also can be generated. So I have {:my-key "my constant string"} (as part of a bigger structure). A spec like this works:

(s/def ::my-key (partial = "my constant string"))
Now I try to generate data inside my test, and this fails. I’m trying to use s/with-gen, but I think I’m missing something.
(s/def ::my-key
  (s/with-gen (partial = "complexity")
              (constantly "complexity")))
yields:
Assert failed: Second arg to such-that must be a generator
(generator? gen)
How should I approach this?

valerauko08:07:08

try #(constantly "complexity")

yuhan08:07:47

You could also use a 1-element set:

(s/def ::my-key #{"complexity"})

aisamu08:07:49

(although the set approach above is the preferred way)

Stefan08:07:14

@UAEH11THP I tried that too, also gives an error. I got it to work though after some trial and error:

(s/def ::my-key
  (s/with-gen (partial = "complexity")
              #(gen/return "complexity")))

Stefan08:07:47

@U1UQEM078 Thanks! Why is that preferred?

aisamu08:07:15

Because then you get the generator for free!

Stefan08:07:17

Oh wait that’s ALL the code I’d need 🙂

Stefan08:07:29

Much simpler indeed 🙂

valerauko08:07:00

wow every day i learn something new

misha10:07:28

yeah, it is idiomatic to spec constants with a single element set. however, if that constant is either false or nil – use false? and nil?, because

(s/valid? #{false} false)
=> false
(s/valid? #{nil} nil)
=> false

Stefan10:07:20

@U051HUZLD I’m probably misunderstanding:

(s/valid? #{false} false)
=> false
(s/valid? #{false?} false)
=> false
(s/valid? #{nil} nil)
=> false
(s/valid? #{nil?} nil)
=> false

misha10:07:38

(s/valid? nil? nil)
=> true

👍 4
misha10:07:58

nil? false? instead of set

yuhan10:07:57

that's an edge case to look out for in general when using sets as predicate functions, which is really all that spec is doing here

yuhan10:07:34

(if (#{1 2 3} x) ;; returns truthy values (1, 2, or 3) if x is in the set
   ...)

 (if (#{false} x) ;; returns a falsey value no matter what x is!
   ...)

misha10:07:27

can be worked around with contains? though