2016-10-01 32 views
1

考虑从新鲜的lein new app arrow-mve的MVE(最小可行示例)命名空间中的以下功能。功能extract-one是公开的,功能extract-two是私人的。我只包括了完整性和为它在我的问题真实entailed远程可能性main-功能:线程箭头clojure.test中的私人defence

(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!")) 

在我的并行测试的命名空间,我可以测试这些功能如下。我可以通过直接调用或使用箭头线宏->来测试公共函数extract-one。另外请注意,在直接调用中,我没有任何提及私有函数extract-two的完整Var的问题。这些测试都通过了:

(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和箭头宏,并想知道它是否是由设计或潜在的错误。当然,在我真正的应用程序(不是这个MVE)中,我有很长很长的调用链,使得使用箭头宏非常可取。

回答

2

下面是答案(不同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)) 

。由于var不是符号,因此if测试似乎对您没有帮助。将var作为函数调用封装在列表中可以解决问题。

我喜欢总是包围函数调用的括号中的线程的形式,并不得使用“裸”功能,即使是通常允许:

(-> 1 
    (inc) ; could have typed "inc" w/o parens 
    (* 2)) ; must use parens since more than 1 arg 
;=> 4