2024 Clojure 状态调查中分享您的想法!

欢迎!请查看关于页面以获取更多关于如何使用该网站的信息。

0
提问 test.check
我正在研究John Hughes的“QuickCheck Testing for Fun and Profit”纸上的示例。我希望测试中生成的结构看起来像这样
 
[:apply `reg [(gen-name) (gen/elements pids)]]

而不是现在必须表达的方式

(gen/tuple (gen/return :apply) (gen/return `reg) (gen/tuple (gen-name) (gen/elements pids)))

我认为可以使用一个简单的递归函数来生成这些,前提是目前生成器创建了一个 maps {:gen #<fn...>},我建议将其更改为一个记录,以便我们可以区分生成器和映射数据结构。

这已经在Quvig QuickCheck中得到很好的实现

bq. 通常,Quviq QuickCheck 允许任何包含嵌入式生成器的数据结构作为该形状的数据结构生成器使用——这对用户来说非常方便,但在Haskell中,将生成器嵌入数据结构通常会导致类型错误。Ericsson编写的生成器广泛使用这种技术。-- Hughes

9 答案

0
回答

评论者:pangloss

今天在REPL上玩耍,我编出了这样一段代码,它可以很好地将任意文字转换为生成器

`
(defprotocol LiteralGenerator
(-literal->generator [literal]))

(defn literal->generator [literal]
(cond

(satisfies? LiteralGenerator literal) (-literal->generator literal)
(vector? literal) (apply gen/tuple (mapv literal->generator literal))
(and (map? literal) (= [:gen] (keys literal)) (fn? (:gen literal))) literal
(map? literal) (gen/fmap (partial into {}) (literal->generator (mapv vec literal)))
(set? literal) (gen/fmap set (literal->generator (vec literal)))
(list? literal) (gen/fmap (partial apply list) (literal->generator (vec literal)))
:else (gen/return literal)))

`

罔顾其用途可能是一般性,所以我将其也添加了

`
(defn record->generator
([record]
;有什么更好的方法吗?
(let [ctor (eval (read-string (str "map->" (last (clojure.string/split (str (class record)) #"\.")))))]

 (record->generator ctor record)))

([ctor record]
(gen/fmap ctor (literal->generator (into {} record)))))
`

这使我能够扩展如下的记录

(defrecord Foo [a b] LiteralGenerator (-literal->generator [this] (record->generator map->AbcDef this)))

我至今还没有考虑将这段代码集成到test.check中的可能性。在进一步推进之前,我想先征求一下大家对目前成果的反馈。

0
by

评论者:reiddraper

所以,我对这件事有点矛盾的想法。我同意这样做很方便,但我同时也担心这样的宽松可能会让更多的错误发生,且不能强迫用户区分值和生成器。例如,如果你做了像这样的事情:{{[int int]}},你原本是想输入{{[gen/int gen/int]}},还是你确实打算写{{[(gen/return int) (gen/return int)]}}这样的等效表达式?如果每个期望生成器的函数都可以接受值,我们就不能再添加错误检查来确保你正确地传递了生成器。同样,我也看到了使用数据结构字面量的好处。如果我们编写一个函数,必须使用它通过这种语法来创建生成器,就像:{{(gen/literal [:age gen/int])}},这样你就可以选择使用这种语法,但只限于gen/literal的作用域内?

0
by

评论者:pangloss

我同意这是一个担忧,由于无法保证看到生成器输出,最好在使用gen/literal时要明确。这仍然是一个不错的解决方案。幸运的是,这正是我所写的!我们只需将literal->generator重命名为{{literal}},并将其安装到clojure.test.check.generators命名空间中。

0
by

评论者:reiddraper

我认为第一步是修改生成器以使用Records。你有兴趣做一个补丁来实现这个吗?

0
by
_评论者:pangloss_

一个非常简单的补丁,使用生成器记录而不是简单的{:gen ƒ) map。
0
by
_评论者:reiddraper_

我已经在版本ef132b5f85a07879f01417c9104aa6dea771fdb4中应用了record-patch。谢谢。我还添加了一个{{generator?}}辅助函数。
0
by

评论者:ppotter

我发现了一个似乎相关的部分:ztellman/collection-check定义了一个(链接:https://github.com/ztellman/collection-check/blob/07ee38e780d54088751dd4834ef9a30866ac5e2d/src/collection_check.clj#L16-L22)函数[target]“tuple”,类似于gen/tuple,但会自动将文字包裹在gen/return中。

值得注意的是,尽管它没有解决一般情况,但这将解决问题描述中的例子。

0
by

评论者:gfredericks

现在有一个库针对此目的:https://github.com/holguinj/jen

0
by
...