Вы можете сделать это с помощью test.check fmap
:
(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]))
(s/def ::parent (s/keys :req-un [::a ::b ::child]))
(gen/sample
(s/gen ::parent
{::parent ;; override default gen with fmap'd version
#(gen/fmap
(fn [{:keys [a b child] :as p}]
(assoc p :child (assoc child :a a :b b)))
(s/gen ::parent))}))
=>
({:a 1, :b 2, :child {:a 1, :b 2}}
{:a 2, :b 2, :child {:a 2, :b 2}}
{:a 1, :b 1, :child {:a 1, :b 1}}
{:a 3, :b 2, :child {:a 3, :b 2}}
{:a 2, :b 4, :child {:a 2, :b 4}}
{:a 4, :b 4, :child {:a 4, :b 4}}
{:a 3, :b 3, :child {:a 3, :b 3}}
{:a 4, :b 4, :child {:a 4, :b 4}}
{:a 3, :b 4, :child {:a 3, :b 4}}
{:a 3, :b 4, :child {:a 3, :b 4}})
fmap
принимает функцию f
и генератор gen
и возвращает новый генератор, который применяет f
каждому значению, сгенерированному из gen
.Здесь мы передаем ему генератор по умолчанию для ::parent
и функцию, которая берет эти родительские карты и копирует соответствующие ключи в карту :child
.
Если вы хотите, чтобы эта спецификация обеспечивала это равенство (кроме простопоколения), вам нужно добавить s/and
к спецификации ::parent
с предикатом, чтобы проверить, что:
(s/def ::parent
(s/and (s/keys :req-un [::a ::b ::child])
#(= (select-keys % [:a :b])
(select-keys (:child %) [:a :b]))))
Изменить: вот еще один способ сделать то же самое с gen/let
, которыйдопускает более "естественный" let
-подобный синтаксис:
(gen/sample
(gen/let [{:keys [a b] :as parent} (s/gen ::parent)
child (s/gen ::child)]
(assoc parent :child (assoc child :a a :b b))))