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,以及将使用什么参数。

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

4

2 回答 2

3

是的,您可以使用load-file. 如果源文件包含命名空间声明,那么这些命名空间现在可供您的 Clojure 应用程序(框架)使用。

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

您所说的问题是您想在事先不知道名称空间名称的情况下从名称空间执行一些测试。有两种方法可以实现这一点:

1) 使用命名约定。即为每个名称与您的约定匹配的名称空间运行您的测试,即

user=> (load-file "/home/noahlz/foo.clj") 
#<Var@1e955d29: #<core$foo foo.test.core$foo@48a7a9bd>>
user=> (filter #(re-matches #".*\.test\..*" %) (map str (all-ns)))
("foo.test.core")

使用上面的代码,您已经获得了可以在其上执行框架代码的命名空间列表。

2) 使用元数据。 与其遵循命名约定,不如要求框架的用户将元数据添加到他们的命名空间。这减少了意外测试意外遵循您的约定的命名空间的机会。

(请参阅:Clojure 元数据有哪些用途?

请注意,这是 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

祝你好运!

于 2013-07-04T17:19:46.743 回答
1

谢谢你的协助。我设法找到了我要找的东西。它实际上比我想象的要简单。不知道这use实际上是一个功能。现在我只是这样做:

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

我从命名空间名称创建一个符号,然后将其传递给use函数。效果很好!

于 2013-07-05T08:17:14.613 回答