Fork me on GitHub
#clojure-spec
<
2016-09-20
>
Oliver George00:09:45

This is fun. Using a pull-query to generate specs means I can generate test data for UI views based on the pull query they use. https://gist.github.com/olivergeorge/da6487fc60c67b79ba082bc807072e43 This still requires table and field specs based on something like this: https://gist.github.com/olivergeorge/468464ce82b8da486736fe725a4b6ff8

danielcompton00:09:29

Is there a JIRA issue open for spec coercions (in the same way that schema does them)? I’m aware of conform, but that isn’t quite the same

Alex Miller (Clojure team)01:09:09

there is no plan to add that

Alex Miller (Clojure team)01:09:35

Rich sees them as separate concerns

otfrom08:09:49

I'm presuming I'm doing something wrong and that there is a function in core that will do this, but this bit of code is working for me to integrate spec tests into my normal clojure.test flow

otfrom08:09:54

(defn passes? [sym]
  (-> sym
      stest/check
      first
      :clojure.spec.test.check/ret
      :result))

(t/deftest conformer-testing
  (t/testing "Checking the conformer"
    (t/is (true? (passes? `sut/my-specced-func)))))

otfrom08:09:14

I've elided the bit where I define the :args :ret and :fn for my-specced-func

otfrom08:09:33

is there something that does the same as passes? and is true? really the right way of putting this into t/is ? Success and failure both return truthy values which is why I'm using it atm

jmglov08:09:41

I have a record that implements a protocol. How do I spec this?

jmglov08:09:14

When I try to gen/sample some deeply nested spec, I get a "Couldn't satisfy such-that predicate after 100 tries." exception with no mention of the spec or predicate in question. Is there a good way to figure out which spec caused this?

jmglov08:09:13

So far, my only hope is a binary search by running gen/sample on the sub-specs until I find the offender. 😞

bahulneel09:09:44

@jmglov I had this problem also. I had to eyeball my specs and try exercising individual ones. In the end, it's usually ones that use and and limit the valid input to some "shape". To fix these I use with gen, an example I stole is here: https://github.com/nwjsmith/datomic-spec/blob/master/src/datomic_spec.clj#L6

jmglov09:09:52

@bahulneel Yes, that was exactly it. My offending one turned out to be (s/and map? empty?). I changed it to #{{}}. 🙂

bahulneel09:09:09

@jmglov it's worth noting that, in that case (s/and empty? map?) will also work fine

jmglov09:09:56

Of course! That's actually nicer to read, I think.

bahulneel09:09:39

the only issue is that the explanation would fail empty? before map? so the message may not be as useful

bahulneel09:09:09

user> (clojure.spec/explain (clojure.spec/and empty? map?) [1])
val: [1] fails predicate: empty?
nil
user> (clojure.spec/explain (clojure.spec/and empty? map?) [])
val: [] fails predicate: map?
nil
user> (clojure.spec/explain (clojure.spec/and empty? map?) {1 2})
val: {1 2} fails predicate: empty?
nil
user> 

bahulneel09:09:25

But it's better than the alternative:

user> (clojure.spec/explain #{{}} {1 2})
val: {1 2} fails predicate: :clojure.spec/unknown
nil
user> (clojure.spec/explain #{{}} [])
val: [] fails predicate: :clojure.spec/unknown
nil
user> 

bahulneel09:09:15

This works better:

user> (clojure.spec/def ::empty-map #{{}})
:user/empty-map
user> (clojure.spec/explain ::empty-map {1 2})
val: {1 2} fails spec: :user/empty-map predicate: #{{}}
nil
user> 

bahulneel09:09:43

(also naming (s/and empty? map?) gives a similar level of clarity)

mpenet09:09:54

any known attempt to generate json-schema from specs?

mpenet09:09:58

there's a ring-swagger fork that tried, but it's not there yet

mpenet09:09:00

I am not really interested in the conform part right now

mpenet09:09:34

I just need generating decent json-schemas from specs

ikitommi10:09:57

ok, not there yet. It would be easy to cook up a working prototype of the conversion from existing spikes, but still gaps to make it robust and extendable. Good thing that, there are smarter people here :)

ikitommi10:09:11

Spectrums parse-spec might be a good way for it.

mpenet10:09:22

Isnt it possible just to gen from s/form?

mpenet10:09:55

It s a one time thing so perf doesnt matter

ikitommi10:09:40

Hmm... there was one version with s/form & core.match, can't recall what was the problem with it. Maybe just not finished.

ikitommi10:09:52

actually, it could be extended too, with multimethod on the spec symbol. Ping @miikka.

mpenet11:09:30

I have something along these lines already, even tho the multimethod is kinda useless ultimately, it can be closed

bhagany12:09:16

@jmglov: regarding protocol implementations - I read (on the mailing list, I think), that directly spec’ing protocol fns wasn’t supported, and the recommendation was to make the protocol fns “private”, provide wrapper fns, and spec those. This worked fine for me, especially because I had some common logic that fit nicely in the wrapper fns.

jmglov12:09:54

@bhagany I'm not trying the spec the protocol functions themselves, but rather an argument to a function that should implement a certain protocol. i.e.

(defprotocol Database
  (read-thing [this id])
  (store-thing [this thing]))

...

(defn do-stuff [db updates]
  (let [thing (database/read-thing (:id updates))]
    (-> thing
        (merge updates)
        database/store-thing)))
I want to spec do-stuff something like this:
(s/fdef do-stuff
        :args (s/cat :db #(implements? Database %)
                     :updates (s/keys :req [::thing/id]
                                      :opt [::thing/name]))
        :ret any?)
Of course implements? is not a real function, but this illustrates what I'm trying to do, I hope.

mpenet12:09:31

you can use satisfies?

jmglov12:09:36

@mpenet Exactly what I needed! Thanks!

mpenet12:09:03

it gets more tricky if you want to add gen support

jmglov12:09:07

No need for now, thank goodness.

bhagany15:09:49

I’m trying to run “free” generative tests in cljs, using doo, but it seems as though the registry is not being populated in my test build. I’m importing a namespace that defines some named specs, and I can run the functions in that namespace, but clojure.spec.test/check and checkable-syms are both empty. However, in my figwheel repl, the registry is populated as I would expect. Any ideas what I might be missing?

bhagany15:09:16

I should also add that clojure.spec.test/enumerate-namespace does see the fns that are spec’d in my test build

bhagany15:09:56

and also that I can (s/valid? ::some/data-spec data) in my test build. It seems like only fn specs are missing.

husain.mohssen15:09:40

Possibly stupid question: why is s/fdef not called s/defn ?

Alex Miller (Clojure team)16:09:34

because that’s not what Rich named it

husain.mohssen16:09:06

good enough for me 🙂 all hail Master Rich 🙂 (I was asking in case there was some subtlety I’m missing)

liamd19:09:00

having a hard time wrapping my head around writing generators, how would i write a generator for a set of distinct strings?

hiredman19:09:52

there is a function, it might be called one-of

hiredman19:09:59

oh, sorry, no wrong one

liamd19:09:27

i tried (gen/fmap set (gen/vector gen/string))

hiredman19:09:00

oh, I misunderstood

hiredman19:09:24

there is a generator for sets

hiredman19:09:41

so (gen/set gen/string)

liamd19:09:07

excellent that did it

hiredman19:09:14

you may need to do some voodoo connect that to spec, I haven't looked too much at how test.check and spec are tied together

liamd19:09:24

i just had to do with-gen

liamd19:09:28

really easy

liamd19:09:40

here’s another one: any idea how to generate long strings?

liamd19:09:45

say > 200 chars?

hiredman19:09:25

if you run more generative test iterations, the size of the strings will get larger, but it would likely be easier to create a special purpose larger string generator

hiredman19:09:50

which maybe some combination of resized and the string generator would help you do

hiredman19:09:45

(gen/set (gen/resized 200 gen/string))

liamd20:09:26

resized looks good

bfabry20:09:01

there's sooooo much stuff in the clojure.test.check.generators namespace, trying to internalise all that is the one thing I'm not looking forward to when we start using spec

liamd20:09:46

the docs don’t mention everything so you gotta dig into the sources

liamd20:09:58

that said, using spec with devcards is kicking ass right now for building om ui's

Alex Miller (Clojure team)20:09:11

in many cases it’s easier to just gen from a spec

Alex Miller (Clojure team)20:09:50

(s/gen (s/coll-of string? :kind set?))

liamd20:09:35

for some specs i get {:message "Couldn't satisfy such-that predicate after 100 tries.", :data {}}

Alex Miller (Clojure team)20:09:56

if you are and’ing specs with fairly restrictive filters, you’re likely to run into this

Alex Miller (Clojure team)20:09:11

this is discussed (plus making custom generators) in http://clojure.org/guides/spec if you haven’t seen that yet

liamd20:09:44

yeah i’ve read that over, seems the solution is to write your own generators and then use with-gen which works great

liamd20:09:21

i’m already re-using a good amount of generators for things like certain numbers and strings with various properties

liamd20:09:49

one thing is that error won’t tell me which generator it’s talking about

liamd20:09:54

which is… confusing

Alex Miller (Clojure team)20:09:58

yes, I think that’s worth improving

Alex Miller (Clojure team)20:09:16

I don’t think anyone has actually filed a ticket about it, but I would be in favor of improving that

liamd20:09:34

is that specific to the cljs implentation or spec in general?

liamd20:09:11

or i guess that’s test.check

Alex Miller (Clojure team)20:09:48

it is, but I suspect it could be wrapped somehow in spec

liamd20:09:06

yeah i suppose you’d want to tie it back to your namespaced key

madstap20:09:48

I'm having a problem with generators.

(def col-gen
  (gen/fmap #(->> % (apply str) str/upper-case keyword)
    (gen/vector gen/char-alpha)))


(s/def :cell/col (s/and simple-keyword? (comp (set util/alphabet) name)))
This throws an error `java.lang.AssertionError Assert failed: Arg to vector must be a generator (generator? generator)` How can I generate a vector of characters from the alphabet?

madstap20:09:58

Other question, how can I make a generator from a set?

hiredman20:09:18

what namespace is gen?

madstap20:09:33

clojure.spec.gen

hiredman20:09:51

I bet you need to invoke gen/char-alpha

hiredman20:09:27

the things in clojure.spec.gen aren't actually generators, they are 0 argument functions that you invoke to get a generator

madstap20:09:50

Thanks, that worked

madstap21:09:39

And how can I make a generator from a set?

hiredman21:09:43

you have a set of elements and you want a generator to pick elements from it?

hiredman21:09:07

gen/elements

Alex Miller (Clojure team)21:09:47

or of course just (s/gen #{:a :b :c})

Alex Miller (Clojure team)21:09:02

people do not leverage gens from specs enough imo

Alex Miller (Clojure team)21:09:18

that should be your first choice

Alex Miller (Clojure team)21:09:54

then stuff in spec.gen, then stuff in test.check

hiredman21:09:34

so (s/gen (s/spec number?)) instead of gen/number or whatever?

Geoffrey Gaillard21:09:29

@alexmiller about that case, is it normal for

(clojure.test.check.generators/sample (cljs.spec/gen #{true false}))
to always be
(true true true true true true true true true true)
?

Alex Miller (Clojure team)21:09:47

well any set with falsey elements is going to be a bad time

Alex Miller (Clojure team)21:09:17

that’s kind of a special case - it will return falsey elements which is the signal that the value doesn’t match the spec

Alex Miller (Clojure team)21:09:31

in this case (s/gen boolean?) will be better

Alex Miller (Clojure team)21:09:41

and if you want to support nil,true,false then (s/gen (s/nilable boolean?))

Geoffrey Gaillard21:09:30

Thank you ! I had some bad time with (s/gen boolean?) in ClojureScript, throwing an error. I figured it out with with-gen and (gen/elements #{true false}). I don't know if it's a known bug or not btw…

Alex Miller (Clojure team)21:09:11

you might ask in #clojurescript to see what @dnolen thinks

dnolen21:09:34

@ggaillard it should work, make sure you are on the latest 1.9.229, if it still doesn’t work, file an issue in JIRA

Geoffrey Gaillard21:09:49

You are quick! I didn't had time to post in #clojurescript 🙂 Thank you, I'll check that.

Geoffrey Gaillard21:09:27

I was using 1.9.216, 1.9.229 fixed it. Sorry for that 😕

devn22:09:58

The clojure.spec/nilable docs say it takes a pred, but it also seems to work if it's passed a spec. Is that behavior I can rely on?