【问题标题】:Threading arrow private defns in clojure.testclojure.test 中的线程化箭头私有定义
【发布时间】:2016-10-01 22:05:37
【问题描述】:

考虑来自新 lein new app arrow-mve 的 MVE(最小可行示例)命名空间中的以下函数。函数extract-one 是公共的,函数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!"))

在我的并行测试命名空间中,我可以按如下方式测试这些函数。我可以通过直接调用或使用箭头线程宏-> 来测试公共函数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}))))

我怀疑问题是通过deftestisVar 的阅读器宏#' 和箭头宏进行宏链接的限制,我想知道这是设计使然还是潜在的漏洞。当然,在我的实际应用程序(不是这个 MVE)中,我有长而深的调用链,这使得使用箭头宏非常可取。

【问题讨论】:

    标签: clojure macros clojure-testing


    【解决方案1】:

    这是答案(不同的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 作为函数调用包含在列表中可以解决问题。

    我更喜欢始终将函数调用括在括号中的线程形式中,并且不使用任何“裸”函数,即使它通常是允许的:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-16
      • 2021-10-10
      • 1970-01-01
      • 1970-01-01
      • 2014-01-15
      • 2014-11-11
      • 2021-12-23
      • 2015-07-13
      相关资源
      最近更新 更多