This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-05-23
Channels
- # aws (4)
- # beginners (14)
- # boot (7)
- # cider (75)
- # clara (87)
- # cljsrn (6)
- # clojure (115)
- # clojure-berlin (2)
- # clojure-dusseldorf (2)
- # clojure-gamedev (8)
- # clojure-italy (15)
- # clojure-russia (9)
- # clojure-spec (46)
- # clojure-uk (195)
- # clojurescript (24)
- # css (44)
- # datascript (19)
- # datomic (18)
- # emacs (6)
- # fulcro (57)
- # hoplon (1)
- # jobs (3)
- # jobs-discuss (37)
- # jobs-rus (3)
- # luminus (6)
- # lumo (28)
- # off-topic (24)
- # onyx (11)
- # planck (8)
- # re-frame (31)
- # remote-jobs (12)
- # rum (10)
- # schema (4)
- # shadow-cljs (28)
- # specter (24)
- # sql (3)
- # tools-deps (34)
- # vim (43)
- # yada (10)
I have something like this:
(spec/def ::groups
(spec/every-kv ::id ::group))
(spec/def ::students
(spec/every-kv ::group/id (spec/every-kv ::id ::student)))
(spec/def ::root
(spec/keys :req [::groups ::students]))
Is there a way for me to specify that when I do (gen/generate (spec/gen ::root))
that every group ID for every student has an equivalent group ID under the ::groups
key?In other words, I'd like every group ID in ::students
to have a corresponding entry in ::groups
. I know how to spec that, but I'm wondering what the best way to write a generator like that is.
I could always just post-process the generated map, but for more complicated maps, that becomes a bit difficult.
I've not used this before, but have heard that test.check has something called bind
that lets you generate one value randomly, e.g. in your case perhaps a set of group IDs, and then use that set to generate other things, e.g. your ::groups map and also the ::students map, which could be generated to have exactly the same set of group IDs in both.
Not sure if the test.check examples of bind
on this doc page are enough to get you going or not: https://clojure.github.io/test.check/generator-examples.html
@flowthing you can generate the groups first, and then (using bind) generate students where their group-id is from (gen/elements (map :id groups))
@flowthing gen/let
can do the same thing, and you may find it more intuitive
Yeah, I've tried gen/let
actually, but I haven't yet come up with a clean solution. Nonetheless, it's good to know I'm heading in the right direction.
hey peoples, i’m not really sure the term/noun for what i’m trying to do with spec, so it’s hard to google… I want to define a spec where an id field that’s present in two places is guaranteed to be the same:
{"id" {:id "id" :other-stuff true}}
I think you would have to have a predicate that for that map, it checks that key and the :id value is the same. And i believe yes, you would have to have a custom generator for it too.
yes and yes
yea i’m not sure how to handle it either, i might just scrap this gen test entirely.. like how do you s/map-of ::id ::some-spec
with also ::some-spec (s/keys :req [::id])
, then write a generator that builds out two(?) specs. maybe i have to merge the map-of?
You could probably use this. https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html#clojure.spec.alpha/spec To create a spec for it
(s/def ::some-spec (s/spec #{200 202 400}
:gen (fn [] (gen/return (gen/generate (s/gen #{200 400 500}))))))
like something like this maybe but catered for making your mapwhere u can supply a spec or a predicate function which would check both id key and value
So I’ve written some specs in an existing project with a clojure.test
test suite.
Does anyone have any recommendations on incorporating generative tests using clojure.spec.test.alpha/check
with clojure.test
?
Obviously I could do:
(t/is (:result (first (clojure.spec.test.alpha/check `foo))
But that will mask the errors.A while back I had a similar issue and tried hooking into the clojure.test
assert macros but it was never very satisfactory
is there a lein
test runner for fdef
s?
actually just remembered I’ve been here before… and have already implemented a lein alias in this project for running specs using instrument etc…
i wrote a little helper for your first problem..
(defn passes?
"A light helper function to evaluate a generative/property test so that
clojure.test spits out the failing result (if any) for easier debugging."
[spec]
(let [res (spec)]
(t/is (nil? (:shrunk res)))
(t/is (true? (:result res)))))
i’m not sure if that’s too implementation-specific with checking shrunk, but it works for me and i didnt have to dig around docs for a long time
@lwhorton what are the arguments?
obviously it’s a function… but I guess I should ask how do you use it?
ah, it’s just like you would use testing
or is/are
:
(defspec my-spec my-prop-test)
(deftest some-test
(testing "some generative test should pass"
(passes? my-spec))))
what is defspec
?
ahh ok — that’s not strictly spec though is it?
Isn’t that a property test defined with test.check
? Rather than through s/fdef
etc
I appreciate spec builds on test-check and that they’re compatible to some degree; but will that pick up an fdef
:fn
spec etc
yea there’s a few moving pieces, the spec itself is defined using clojure.test.check.properties
and a generator that uses spec/gen
this was pretty helpful for me: https://github.com/clojure/test.check#examples
particularly:
(defspec first-element-is-min-after-sorting ;; the name of the test
100 ;; the number of iterations for test.check to test
(prop/for-all [v (gen/not-empty (gen/vector gen/int))]
(= (apply min v)
(first (sort v)))))
yes I’ve seen that before; but I’m not sure it’s what I need — e.g. if you had a spec defined like this from the clojure docs:
(s/fdef ranged-rand
:args (s/and (s/cat :start int? :end int?)
#(< (:start %) (:end %)))
:ret int?
:fn (s/and #(>= (:ret %) (-> % :args :start))
#(< (:ret %) (-> % :args :end))))
that spec is already def
ed so to speak; as it’s metadata is registered already with the system.
@lwhorton: 🙂 no worries; it’s subtle stuff
I have used defspec
before… and tbh it seems like it’s a better way to integrate with clojure.test
right now
@lwhorton in the interests of sharing I have something like this that works as a different entry point i.e. run via a lein
alias:
it uses st/instrument
to enable all the fdef
specs and run them through st/check