2024 年 Clojure 调查问卷! 中分享您的想法。

欢迎!请参阅 关于 页面了解如何操作的更多信息。

0
test.check

Haskell QuickCheck 允许 for-all 表达式嵌套。这在生成值之间存在依赖关系时很有用。test.check 也应该允许这样。

目前,嵌套的 for-all 总是成功,这在某种程度上是有害的。

我已经添加了一个实现此功能的补丁。

9 个答案

0

评论者:reiddraper

谢谢 Michael。我很感激这个补丁,但在我们进入代码级细节之前,可以讨论一些设计细节。作为一个与之相关但分开的问题,我一直想实现类似于您的 {{CheckResult}} 类型,但作为一个具有更多字段的记录。这些字段将包含诸如任何错误发现的纯文本描述、统计数据(类似于 QuickCheck 的 {{collect}}、{{classify}} 等)。我还想编写一个协议,该协议允许基本类型(如布尔值)转换为此记录。这将类似于 Haskell QuickCheck 的 {{Testable}} 类型类。虽然这是一个技术上的单独问题,但我认为我们应该与可嵌套 for-all 一起解决,尤其是在嵌套的 for-all 只需使用生成器级别的 bind 就可以模拟的情况下。这说得通吗?

0

评论者:sperber

当然。

我个人会从这个补丁开始,然后继续改进,除非您想在本质上做出不同的事情,而不仅仅是添加更多内容。

无论如何,我怎样才能帮忙让它变成现实呢?

0

评论者:reiddraper

这是一个好问题。我会就此问题进行思考,并尽快回复您。我也希望能够尽快实现这个功能。

0
评论者:reiddraper

抱歉延迟回复,这里是我在合作的草图


diff --git a/src/main/clojure/clojure/test/check/properties.clj b/src/main/clojure/clojure/test/check/properties.clj
index 99b5222..139ae9a 100644
--- a/src/main/clojure/clojure/test/check/properties.clj
+++ b/src/main/clojure/clojure/test/check/properties.clj
@@ -8,13 +8,47 @@
 ANTED   请不要从本软件中删除此通知或任何其他通知。
 
 (ns clojure.test.check.properties
+  (:import clojure.test.check.generators.Generator)
   (:require [clojure.test.check.generators :as gen]))
 
+(defrecord Result [result pass? message stamps])
+
+(defprotocol ToResult
+  (to-result [a]))
+
+(extend java.lang.Object
+  ToResult
+  {:to-result (fn [b]
+              ";;    此处不检查捕获的异常
+               (->Result b (not (false? b)) nil nil))})
+
+(extend nil
+  ToResult
+  {:to-result (fn [b]
+               (->Result b false nil nil))})
+
+(extend java.lang.Boolean
+  ToResult
+  {:to-result (fn [b]
+               (->Result b b nil nil))})
+
+(extend Generator
+  ToResult
+  {:to-result identity})
+
+(extend Result
+  ToResult
+  {:to-result identity})
+
+(defn message
+  [m property]
+  (assoc property :message m))
+
 (defn- apply-gen
   [function]
   (fn [args]
-    (let [result (try (apply function args) (catch Throwable t t))]
-      {:result result
+    (let [result (to-result (try (apply function args) (catch Throwable t t)))]
+      {:result (:result result)
        :function function
        :args args})))
 
@@ -29,9 +63,18 @@
   (for-all* [gen/int gen/int] (fn [a b] (>= (+ a b) a)))
   "
   [args function]
-  (gen/fmap
-    (apply-gen function)
-    (apply gen/tuple args)))
+  (gen/bind
+    (apply gen/tuple args)
+    (fn [a]
+      (let [result ((apply-gen function) a)]
+        (cond (gen/generator? result) (gen/fmap (fn [r] (println "foo") (update-in r :args #(conj % a))) result)
+              ; NOTE: quick note to myself before I leave this code for the night,
+              ;; this :else is getting hit because we're wrapping the result
+              ;; with a {:result ...} map. Should probably do that conditionally.
+              ;; We also need two result types I think, a result to return from
+              ;; a property itself, and a reuslt that tacks the 'args' on top of this.
+              :else (do (println "bar") (gen/return result)))))
+    ))
 
 (defn 绑定变量
   [bindings]
0
by

评论者:sperber

看起来没问题。然而,很难看出为什么这会比我的补丁更快地把你带到你想去的地方……

0
by

评论者:reiddraper

bq. 看起来没问题。然而,很难看出为什么这会比我的补丁更快地把你带到你想去的地方……

很公平。其中一部分原因是,我写草图更容易一些。我在支持嵌套生成器时主要试图涵盖以下几个要点:确保

  1. 我们支持即将推出的收集和返回有关测试的统计信息的能力,类似于Haskell QuickCheck中的{{collect}}
  2. 我们有一个合适的方式来将失败的测试返回给用户。目前,在返回映射的{{:fail}}和{{:smallest}}键中,我们告诉用户失败的参数。由于您可能使用多个使用{{prop/for-all}}的生成器,它们总是至少被一个向量封装。对于嵌套属性,我们应该怎么办?我们应该如何区分在“同一级别”上多个生成器和嵌套属性?或者我们不需要区分?我们所决定的任何事情都应该是向后兼容的?

重点是,我想确保我们在没有这些答案之前不要使自己承诺嵌套属性,对我个人来说,尝试一起玩这些事情更容易,看看它们是如何融合在一起的。

0
by
_由:expez_发表的评论

我真的不想嵌套for-all,我更希望它像let那样工作,我们可以引用前值。话虽如此,不管哪种解决方案,这都是我对test.check目前的最大抱怨,所以任何解决方案都优于另一年的躺椅时间。

这是一个解决方案,取自野外。我相信还有很多其他方法,人们非常需要这个功能,但这个是从Nathan Marz的specter库中取出的


(defmacro for-all+ [bindings & body]
  (let [parts (partition 2 bindings)
        vars (vec (map first parts))
        genned (reduce
                (fn [curr [v code]]
                  `(gen/bind ~code (fn [~v] ~curr)))
                `(gen/return ~vars)
                (reverse parts))]
    `(prop/for-all [~vars ~genned]
                   ~@body )))
0
by

评论者:gfredericks

我们一直在考虑一个类似的问题(链接:http://dev.clojure.org/jira/browse/TCHECK-81 文本:TCHECK-81),同时还有一个更复杂的 {{for-all}} 的变体(链接:https://github.com/gfredericks/test.chuck#properties 文本:这里)。

0
by
参考:https://clojure.atlassian.net/browse/TCHECK-44(由 alex+import 报告)
...