test.check: поведение в стиле let в свойствах / для всех
Следуя этому вопросу и сообщению в блоге, есть причина, почему prop/for-all
не просто катиться в такой возможности напрямую? Например, что-то вроде:
(require '[clojure.test.check.generators :as gen])
(require '[clojure.test.check.properties :as prop])
(require '[clojure.test.check.clojure-test :refer :all])
(defspec some-props-test
(prop/for-all [n (gen/choose 1 10)
v (gen/vector gen/int n) ;; treat n like its produced value
e (gen/element v)]
... do stuff with n, v & e ...
))
По сути, я хочу повторно использовать значение, полученное одним генератором в другом генераторе, а затем ссылаться на оба значения, полученные в реальном тестовом коде. Это существенно расширит сахар / магию for-all
в разрешении ссылок на сгенерированные значения в блоке let-like, предоставляемом макросом, так как он работает в блоках выражений ниже.
Пожалуйста, дайте мне знать, если я упускаю что-то еще, что делает это возможным, или это просто не имеет смысла по какой-то причине для реализации.
2 ответа
Я согласен, что эта функциональность, вероятно, будет более полезной, чем что for-all
в настоящее время делает. Основная причина, по которой он не был изменен, заключается в обратной совместимости (хотя, по общему признанию, код, использующий старый стиль, не сломался бы, он просто не уменьшился бы так, как раньше).
Но у вас есть больше возможностей, чем просто монады:
gen/let
, который используетlet
в стиле связывания (это не вставная замена дляfor-all
но вы можете использовать их вместе)com.gfredericks.test.chuck.generators/for
определено в вспомогательной библиотеке test.chuck - это как более модная версияgen/let
com.gfredericks.test.chuck.properties/for-all
в той же библиотеке, которая является заменой дляfor-all
Я нашел более позднюю запись в блоге в этой серии, которая полностью очищает использование от test.check
(нужно было немного прочитать monads
первый, чтобы прогнать его). Итак, сначала можно объявить монаду:
(require '[clojure.algo.monads :as m])
(m/defmonad gen-m
[m-bind gen/bind
m-result gen/return])
(def vector-and-elem
(m/domonad gen-m
[n (gen/choose 1 10)
v (gen/vector gen/int n)
e (gen/element v)]
[n v e]))
gen-m
monad позволяет ссылаться на значение, которое будет сгенерировано для ранее объявленных генераторов.
Затем он может быть использован непосредственно в for-all
вызов:
(defspec some-props-test
(prop/for-all [[n v e] vector-and-elem]
... do stuff with n, v & e ...
))
Вы можете просто передать все значения, которые имеют отношение к вашему коду проверки ограничений, в выражении, созданном gen-m
вызов монады через вектор (или карту, если хотите) и де-структурирование, чтобы получить то, что вам нужно.
Тем не менее, было бы неплохо, если бы это было сделано автоматически в for-all
, но это работает достаточно хорошо.