2016-10-01 30 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