This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-02-02
Channels
- # announcements (2)
- # beginners (69)
- # boot (1)
- # cider (35)
- # clara (1)
- # cljs-dev (1)
- # clojure (40)
- # clojure-spec (11)
- # clojure-uk (8)
- # clojurescript (9)
- # cursive (1)
- # datomic (5)
- # figwheel-main (25)
- # fulcro (64)
- # jobs (2)
- # jukebox (1)
- # kaocha (3)
- # off-topic (7)
- # pathom (39)
- # planck (5)
- # random (1)
- # re-frame (11)
- # reagent (8)
- # shadow-cljs (58)
- # test-check (41)
- # vim (13)
Hi. I try to write a test and stuck with generator/bind semantics. Say I want to write a test map's get (my actual goal is more complex, this one just a good showcase).
and i can use it like this
(gen/sample (keys-from (gen/generate (gen/map gen/keyword gen/nat))))
Now I can write a property
(def prop-test
(prop/for-all [map (gen/map gen/keyword gen/nat)
key (keys-from map)]
(get map key)))
This from (defspec ..), (tc/quick-check 1 prop-test) yields completely different failure.
My another attempt
(defn keys-from-gen [map-gen]
(gen/let [map (gen/such-that (complement empty?) map-gen)]
(gen/elements (keys map))))
(gen/sample (keys-from-gen (gen/map gen/keyword gen/nat))) ; works well
(def prop-test-gen
(prop/for-all [map (gen/map gen/keyword gen/nat)
key (keys-from-gen map)]
(get map key)))
(defspec map-gen-test 2 prop-test-gen)
fails with
Uncaught Error: Assert failed: Second arg to such-that must be a generator
So symbols in for-all
bound to neither real values nor generators. I am totally confused. How do I write such property?
@cheatex the confusion comes from the fact that the binding names in prop/for-all
aren't visible at all to the expressions for subsequent bindings
(i.e., they're all done "in parallel")
normally you'd get whatever error you normally get when you use a name that's not visible, but in this case you're getting more confusing errors because you're happening to use the name map
, which already means cljs.core/map
so you're passing the map
function to get
prop/for-all
doesn't have a way to get the bind
semantics, so you have to do this with an additional generator to help you compose things
(def gen-map-and-key (gen/let [m (gen/map gen/keyword gen/nat), k (keys-from-gen m)] {:map m :key k}))
I've made this kind of generator for my problem, but i'm not perfectly happy with it. It basically needs to generate a huge data structure to create a key for it... and than throw it away and create a new one just to make another key.
For each trial, you mean?
I've had idea of generating [map [&keys]] but that would make properties to be a huge and
and hard to find exact failing key.
you don't have to spell out the and
, you could use every?
e.g., you could check every key
and as long as the failing map entry is independent of the others, it should shrink to a singleton map
will it be somewhat independent? like should shrinking remove most of the entries?
if that's a big problem, you could use the subsequence
generator from this library https://github.com/gfredericks/test.chuck
to get a subset of the keys
and then that would shrink to a subset with only one failing key
But generally. Is it impossible to use some generated value to set up other generator, use both value and generator in a property and than go to new one?
I'm not sure what you mean by "and than than go to new one"
So in my example when I run property check with size 50 it (just sample numbers to show magnitudes) generates 5 maps and 10 keys for every map.
there's a lot of ways to control the size or complexity of a generator
it might help to look at https://github.com/clojure/test.check/blob/master/doc/growth-and-shrinking.md