I'm trying to learn how to use overrides
with s/gen
.
I have a ::parent
map which contains a ::child
map. Both parent and child have keys in common. The requirement is that the keys have the same value between parent and child, e.g. {:a 1 :b 2 :child {:a 1 :b 2}
. I know this seems redundant, but the problem domain requires it.
The code below generates examples, but the requirement above is not met.
Is there a way to use the same generated value in two locations?
(ns blah
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen]))
(s/def ::a (s/int-in 1 5))
(s/def ::b (s/int-in 1 6))
(s/def ::child
(s/keys :req-un [::a ::b]))
(defn- parent-gen []
(let [a #(s/gen ::a)
b #(s/gen ::b)]
(s/gen ::parent-nogen
; overrides map follows
{::a a ::b b
::child #(s/gen ::child
; another overrides map
{::a a ::b b})))
(s/def ::parent-nogen
(s/keys :req-un [::a ::b ::child]))
(s/def ::parent
(s/with-gen ::parent-nogen parent-gen))
(gen/sample (s/gen ::parent))
You can do this with test.check's
fmap
:fmap
takes a functionf
and a generatorgen
, and returns a new generator that appliesf
to every value generated fromgen
. Here we pass it the default generator for::parent
, and a function that takes those parent maps and copies the appropriate keys into the:child
map.If you want this spec to enforce that equality (besides just generation), you'll need to add an
s/and
to the::parent
spec with a predicate to check that:Edit: here's another way to do the same thing with
gen/let
that allows for a more "natural"let
-like syntax: