clojure.test 中的线程箭头私有定义
Threading arrow private defns in clojure.test
考虑来自新鲜 lein new app arrow-mve
的 MVE(最小可行示例)命名空间中的以下函数。函数extract-one
是public,函数extract-two
是私有的。我包含了 main-
函数只是为了完整性以及它在我的问题中所涉及的可能性很小:
(ns arrow-mve.core
(:gen-class))
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
在我的并行测试命名空间中,我可以按如下方式测试这些函数。我可以通过直接调用或使用箭头线程宏 ->
来测试 public 函数 extract-one
。另请注意,我在直接调用中通过完整的 Var
引用私有函数 extract-two
没有问题。这些测试通过:
(ns arrow-mve.core-test
(:require [clojure.test :refer :all]
[arrow-mve.core :refer :all]))
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a
(is (= 1 (#'arrow-mve.core/extract-two
{:a 1, :b 2}))))
但是当我试图用箭头宏调用私有函数 extract-two
时出现编译错误:
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.RuntimeException: Unable to resolve
var: arrow.mve.core/extract-two in this context, compiling:
(arrow_mve/core_test.clj:10:12)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
当我使测试稍微复杂一点时,事情变得更奇怪了。
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(-> {:a {:x 3.14, :y 2.72}, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.ClassCastException:
clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.Symbol,
compiling:(arrow_mve/core_test.clj:18:10)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
同样,测试以直接调用形式通过:
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(#'arrow-mve.core/extract-two
{:a {:x 3.14, :y 2.72}, :b 2}))))
我怀疑问题是通过 deftest
、is
、Var
的 reader 宏 #'
和宏链接的限制箭头宏,想知道它是设计使然还是潜在的错误。当然,在我的实际应用程序(不是这个 MVE)中,我有很长很深的调用链,这使得使用箭头宏非常可取。
答案如下(ns不同):
主命名空间:
(ns clj.core
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
测试命名空间:
(ns tst.clj.core
(:use clj.core
clojure.test )
(:require [tupelo.core :as t]))
(t/refer-tupelo)
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a1
(is (= 1 (#'clj.core/extract-two {:a 1, :b 2}))))
;(deftest test-two-b
; (is (= 1 (-> {:a 1, :b 2}
; clj.core/extract-two)))) ; fails: not public
;(deftest test-two-b1
; (is (= 1 (-> {:a 1, :b 2}
; #'clj.core/extract-two))))
; fails: can't cast PersistentArrayMap to Symbol
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
(#'clj.core/extract-two))))) ; works
答案是 var 引用需要放在括号内。线程宏都有一个测试形式(伪代码):
(if (not (list? form))
'(form)
form)
所以像这样的形式
(-> 1
inc)
转化为
(-> 1
(inc))
在其余线程发生之前。 if
测试似乎对您来说失败了,因为 var 不是符号。将 var 作为函数调用包含在列表中可以解决问题。
我更喜欢始终将线程形式的函数调用括在括号中,并且不使用任何 "naked" 函数,即使它通常是允许的:
(-> 1
(inc) ; could have typed "inc" w/o parens
(* 2)) ; must use parens since more than 1 arg
;=> 4
考虑来自新鲜 lein new app arrow-mve
的 MVE(最小可行示例)命名空间中的以下函数。函数extract-one
是public,函数extract-two
是私有的。我包含了 main-
函数只是为了完整性以及它在我的问题中所涉及的可能性很小:
(ns arrow-mve.core
(:gen-class))
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
在我的并行测试命名空间中,我可以按如下方式测试这些函数。我可以通过直接调用或使用箭头线程宏 ->
来测试 public 函数 extract-one
。另请注意,我在直接调用中通过完整的 Var
引用私有函数 extract-two
没有问题。这些测试通过:
(ns arrow-mve.core-test
(:require [clojure.test :refer :all]
[arrow-mve.core :refer :all]))
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a
(is (= 1 (#'arrow-mve.core/extract-two
{:a 1, :b 2}))))
但是当我试图用箭头宏调用私有函数 extract-two
时出现编译错误:
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.RuntimeException: Unable to resolve var: arrow.mve.core/extract-two in this context, compiling: (arrow_mve/core_test.clj:10:12) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875) at clojure.lang.Compiler.analyze(Compiler.java:6669) at clojure.lang.Compiler.analyze(Compiler.java:6625)
当我使测试稍微复杂一点时,事情变得更奇怪了。
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(-> {:a {:x 3.14, :y 2.72}, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.Symbol, compiling:(arrow_mve/core_test.clj:18:10) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875) at clojure.lang.Compiler.analyze(Compiler.java:6669) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
同样,测试以直接调用形式通过:
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(#'arrow-mve.core/extract-two
{:a {:x 3.14, :y 2.72}, :b 2}))))
我怀疑问题是通过 deftest
、is
、Var
的 reader 宏 #'
和宏链接的限制箭头宏,想知道它是设计使然还是潜在的错误。当然,在我的实际应用程序(不是这个 MVE)中,我有很长很深的调用链,这使得使用箭头宏非常可取。
答案如下(ns不同):
主命名空间:
(ns clj.core
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
测试命名空间:
(ns tst.clj.core
(:use clj.core
clojure.test )
(:require [tupelo.core :as t]))
(t/refer-tupelo)
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a1
(is (= 1 (#'clj.core/extract-two {:a 1, :b 2}))))
;(deftest test-two-b
; (is (= 1 (-> {:a 1, :b 2}
; clj.core/extract-two)))) ; fails: not public
;(deftest test-two-b1
; (is (= 1 (-> {:a 1, :b 2}
; #'clj.core/extract-two))))
; fails: can't cast PersistentArrayMap to Symbol
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
(#'clj.core/extract-two))))) ; works
答案是 var 引用需要放在括号内。线程宏都有一个测试形式(伪代码):
(if (not (list? form))
'(form)
form)
所以像这样的形式
(-> 1
inc)
转化为
(-> 1
(inc))
在其余线程发生之前。 if
测试似乎对您来说失败了,因为 var 不是符号。将 var 作为函数调用包含在列表中可以解决问题。
我更喜欢始终将线程形式的函数调用括在括号中,并且不使用任何 "naked" 函数,即使它通常是允许的:
(-> 1
(inc) ; could have typed "inc" w/o parens
(* 2)) ; must use parens since more than 1 arg
;=> 4