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

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

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