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

欢迎!有关如何使用此功能的更多信息,请参阅 关于 页面。

+1 投票
Multimethods

以下行为让我感到惊讶
1. 定义一个调用 (is (= 在单项上的测试
2. 重写多方法 clojure.test/assert-expr 以自定义对 = 的测试,使其在只有一个项时出错
3. 运行测试,由于它不会出错,所以不会调用多方法
4. 重新定义测试
5. 现在将调用新的多方法

我相信有一个更小的复现案例,但这是我遇到的真实世界的场景。CIDER 改变了中间件中的这种报告,但中间件仅在启动后和需要时动态加载。因此,此行为仅在您使用 CIDER 的测试功能和 重新定义测试 后才会出现。

我在想,这是否是预期的行为,因为 deftest 盒子会把测试存入元信息,或者这实际上是一个缺陷。

(require '[clojure.test :refer [deftest is assert-expr]])
(deftest foo (is (= (println 1))))

(foo) ;; no error

(defn =-body
  [msg expected more]
  (if (seq more)
    `(let [more# (list ~@more)
           expected# ~expected
           result# (apply = expected# more#)]
       (->> (if result#
              {:type :pass}
              {:type :fail
               :diffs (->> (remove #(= expected# %) more#)
                           (map #(vector % (data/diff expected# %))))})
            (merge {:message ~msg
                    :expected expected#
                    :actual more#})
            test/do-report)
       result#)
    `(throw (Exception. "= expects more than one argument"))))

(defmethod assert-expr '= [msg [_ expected & more]]
  (=-body msg expected more))

(foo) ;; no error
(deftest foo (is (= (println 1))))
(foo) ;; error

1 答案

+1 投票

由于 is 是一个宏,它在编译时使用 assert-expr 多方法来展开代码,因此在运行 deftest 之后更改 assert-expr 将不会更改该测试函数的行为,因为该函数已经展开和编译。

这就是为什么您在定义新的测试(或重新定义现有的测试)之前看不到新的 assert-expr 功能。

...