2013-07-04 40 views
3

我正在使用Clojure编写一个小测试框架。在运行时导入脚本

(ns pvt.core.runner 
    (use 
    [pvt.tests.deployment] 
    [pvt.tests.files] 
    [pvt.tests.jms])) 

(defn- run-test 
    [test-name] 
    {test-name (test-and-log test-name)}) 

(defn- run-all-tests-in-namespace 
    [namespace-name] 
    (map 
    run-test 
    (vals (ns-publics (symbol namespace-name)))) 
) 

(defn run-all-tests 
    [namespace-list] 
    (map run-all-tests-in-namespace namespace-list)) 

run-all-tests函数接受Clojure的脚本列表,加载这些脚本中所有的公共职能和运行它们。这很好,只是我必须实际导入这些脚本。我打电话给我的函数(run-all-tests ["pvt.tests.deployment" "pvt.tests.files" "pvt.tests.jms"]),但这只适用于我导入这些脚本的每一个,如我的代码摘录开头所示。这是不行的,因为我不知道谁会调用run-all-tests,以及将使用哪些参数。

我想知道是否有在运行时导入这些脚本的方法。我已经知道每个脚本的命名空间,所以我拥有所有必需的信息。这可以做到吗? 谢谢

+1

命名空间是一等公民。我不明白你的问题是什么。请参阅:http://clojure.org/namespaces和http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/use – dsm

+0

嗯,命名空间是一等公民,但是这些技术需要去做sebi想要的东西对初学者来说可能不是那么明显。请适当地查看我的回答和评论(特别是关于如何映射和过滤名称空间的任何反馈)。 – noahlz

+0

你的回答非常好,全面,但我猜想所有需要的是在函数内部调用(使用...)。 OP的回答证实了这种情况。 – dsm

回答

1

谢谢你帮我。我设法找到了我正在寻找的东西。这实际上比我想象的要简单。不知道use实际上是一个函数。现在,我只是这样做:

(defn- run-all-tests-in-namespace 
    [namespace-name] 
    (use (symbol namespace-name)) 
    (map 
    run-test 
    (vals (ns-publics (symbol namespace-name)))) 
) 

我创建的命名空间名称的符号,然后将它传递给use功能。很棒!

+0

哈,真的吗?你的问题表明你正在创建一个框架,并且不想硬编码命名空间名称。 – noahlz

+0

我不知道。 run-all-tests是一个公共方法,它将接收来自调用者的名称空间名称。 – sebi

+0

好了,好了,你应该更喜欢'(需要)'和':参见[测试FNS ...]'来代替:http://stackoverflow.com/questions/10358149/in-clojure-1-4-what-是在需求中引用的 - 我不认为你真的想使用'use' – noahlz

3

是的,您可以使用load-file从任意文件路径导入Clojure源文件。如果源文件包含名称空间声明,则这些名称空间现在可供您的Clojure应用程序(框架)使用。

显然,至少您必须编写一些代码,它们要么从命令行中获取Clojure源文件的名称,要么指向源文件所在的目录。然后你的代码将使用(load-file)加载文件。

您声明的问题是,您希望从名称空间执行一些测试,而无需事先知道名称空间名称。有两种方法可以实现:

1)使用命名约定。即运行测试对于其名称符合约定每个命名空间,即

user=> (load-file "/home/noahlz/foo.clj") 
#<[email protected]: #<core$foo [email protected]>> 
user=> (filter #(re-matches #".*\.test\..*" %) (map str (all-ns))) 
("foo.test.core") 

使用像上面的代码中,你得到的命名空间时,你可以执行你的框架代码列表。

2)使用元数据。而不是遵循命名约定,要求您的框架的用户将元数据添加到其名称空间。这减少了意外测试偶然遵循惯例的命名空间的机会。

(参见:What are some uses of Clojure metadata?

注意,这是由Clojure的自己clojure.test/deftest宏所使用的方法。

以下是使用自定义元数据查找命名空间的示例。您的命名空间声明在源文件中定义的测试:

(ns ^{:doc "some documentation" :my-framework-tests true} 
    foo.test.core) 

在REPL,你如何可以以编程方式获得这些例子:

user=> (load-file "foo.clj") 

user=> (filter (fn [[n m]] (:my-framework-tests m)) 
       (map #(vector (str %) (meta %)) (all-ns))) 
(["foo.test.core" {:my-framework-tests true, :doc "some documentation"}]) 

现在你有已被标记为命名空间的列表包含您的自定义测试框架的测试。你甚至可以在命名空间函数中使用元数据,以避免为这些命名约定。

可能有一个更简洁的方式来获取具有某些元数据的名称空间(如果有人知道它,通过一切手段,评论!)

另一个重要提示:我加载任意文件来证明这是可能的,买你真的应该考虑以下约定其次Leiningen时,Maven或其他构建框架。例如,看到lein-perforate

祝你好运!

相关问题