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

欢迎!请参阅 关于 页面获取有关此如何工作的更多信息。

0
Spec

当尝试手动连接 clojure.test 和 spec 以用于 fdef'ed 函数时,我发现 spec.test/check 返回一个地图,其中包含 (link: :failure false) 对于失败的测试。我(几乎)确信它应该返回 true,因为 spec.test/abbrev-result 接收 spec.test/check 的返回值作为参数,并测试 :failure 的值。我无法生成 spec.test/check 返回 (link: :failure true) 作为失败的测试的案例。为了信息,对于通过测试返回没有 :failure 键的地图。

以下是一个简单的 REPL 重复案例:

(defn foo-fn (link: x) "bar")

(s/fdef foo-fn

    :args (s/cat :x any?)
    :ret string?)

(stest/check `foo-fn) ;; => 没有错误,打印正常的小地图,没有 :failure 条目

(defn foo-fn (link: x) 1)

(stest/check `foo-fn) ;; => 完整错误地图,:failure 等于 false. :failure 显示如下

(-> (stest/check `foo-fn) first :failure) ;; => false

9 个答案

0

由 gfredericks 评论:

这虽不是一个错误,但它确实有点令人困惑。

{{stest/check}} 正在传递 {{clojure.test.check/quickcheck}} 的返回值的一部分,{{clojure.test.check/quickcheck}} 返回被评估为真假的属性返回值。由于非真值只能为 {{false}} 或 {{nil}},因此查看它并不能学到很多东西(尽管 {{:failure}} 键下也可能有一个异常,这将更有信息量)。

test.check 的下一个版本应该以一种更有用且更简单的方法从测试中提取信息,但我不确定使用 {{stest/check}} 时这会是什么样子。

0
作者:

评论者:djebbz

我还是不明白为什么你不认为这是一个错误。`stest/check`的文档字符串表明它是“可选测试失败”。我期望只在测试失败时才有`:failure`键(因此称为“可选”),并带有一些有用的信息。

我用`stest/abbrev-result`显示`stest/check`的输出,它期望`:failure`为真来显示所有有用的失败信息。如果`stest/check`返回`false`,这就是我认为的矛盾之处,即错误。`stest/abbrev-result`的文档字符串明确表示它接收检查结果作为参数,因此我没有生搬硬套。

感谢你如此快速地回答我,并抽出宝贵的时间。

0
作者:
评论者:grzm

对于`:failure`的`false`值确实很令人困惑。`stest/abbrev-result`在`{{:failure false}}`中也是很令人困惑的,因为它没有提供通常失败情况下的额外信息,正如[djebbz]所指出的。

https://github.com/clojure/spec.alpha/blob/2824ad49df8deadcb4b75acdf624e732a85b4ac7/src/main/clojure/clojure/spec/test/alpha.clj#L438-L446
(defn abbrev-result
  "给定检查结果,返回适合摘要使用的简化版本。"

  [x]
  (if (:failure x)
    (-> (dissoc x ::stc/ret)
        (update :spec s/describe)
        (update :failure unwrap-failure))
    (dissoc x :spec ::stc/ret)))


以下是一个示例,说明它会多么具有误导性


(require '[clojure.spec.alpha :as s]
         '[clojure.spec.test.alpha :as stest])

(alias 'stc 'clojure.spec.test.check)

(defn adder [a b]
  (+ a b))

(s/fdef adder
        :args (s/cat :a int? :b int?)
        :ret string?)

(-> (stest/check `adder) first stest/abbrev-result)
;; => {:sym ex.check-test/adder, :failure false}

;; 编写一个检查 `true` 的 `abbrev-result` 的替代版本


(defn- failure-type [x] (::s/failure (ex-data x)))
(defn- unwrap-failure [x] (if (failure-type x) (ex-data x) x))

(defn- abbrev-result [x]
  (let [failure (:failure x)]
    (if-not (or (true? failure)
                (nil? failure))
      (-> (dissoc x ::stc/ret)
          (update :spec s/describe)
          (update :failure unwrap-failure))
      (dissoc x :spec ::stc/ret))))

(-> (stest/check `adder) first abbrev-result)
;; => {:spec (fspec :args (cat :a int? :b int?) :ret string? :fn nil),
;;     :sym ex.check-test/adder,
;;     :failure false}


再次注意,任何真实的 {{:failure}} 值都将提供额外的详细信息,而假的 {{:failure}} 值则不会。

我理解不更改 {{:failure}} 键值的动机。如果该值要维持不变,我认为 {{stest/abbrev-result}} 应该相应地更新,以便明确测试 {{nil}} 和 {{true}},而不是测试 truthy 以保持与 {{stest/check}} 结果的一致性。

0

评论者:djebbz

非常感谢 Michael. 最后我选择了一个小的变化,其中我不 (dissoc x ::stc/ret),而是保留整个检查结果,因为它包括堆栈跟踪、spec/explain-data 映射以及缩小的失败用例等。

0

评论者:grzm

看起来 {{abbrev-result}} 应该使用 {{result-type}} 来测试检查是否通过。附上一个补丁,实现了这个功能。如果您希望有相应的测试,我很乐意重新提交。

编辑:我太急了。我会进行调查并重新提交。对此带来的打扰表示歉意。

编辑 2:我对自己的怀疑是不正确的。我非常确定第一次我是正确的。我认为这个补丁很好。

0

由 gfredericks 评论:

请注意, master 分支的 test.check 存在尚未发布的更改,可能会影响这里最好的做法。》(链接:https://github.com/clojure/test.check/blob/master/src/main/clojure/clojure/test/check/results.cljc 文本:这些内容)

0
评论者:grzm

感谢 [~,gfredericks]。Result 协议是值得关注的事情。我认为在 {{abbrev-result}} 层面上使用 {{result-type}} 是正确的抽象。我认为在 {{make-check-result}} 中装饰 {{quick-check}} 结果时,会使用 {{Result/passing?}} 而不是使用 [{{(true? result)}}|https://github.com/clojure/spec.alpha/blob/2824ad49df8deadcb4b75acdf624e732a85b4ac7/src/main/clojure/clojure/spec/test/alpha.clj#L319] 调用。这对您有意义吗?
0

由 gfredericks 评论:

是的,我认为是这样。但现在我已经审查了规范代码,并看到有多少细节关于返回值被依赖(这也可能适用于其他test.check用户,也不应该让我感到惊讶),我想我将为{{quick-check-2}}(链接:TCHECK-142 文字:https://dev.clojure.org/jira/browse/TCHECK-142)保留结果协议的详细资料。

0
引用:[CLJ-2246](https://clojure.atlassian.net/browse/CLJ-2246)(由ale inventions报告)
...