11

我正在尝试为我的 clojure 函数编写一些单元测试(我正在使用 clojure.test,但如果需要,我可以切换到 midje)。

我有一个函数,如下所示:

(defn GenerateNodes
   [is-sky-blue? hot-outside? name]
   (cond
     (is-sky-blue? name) (generate-sky-nodes)
     (hot-outside?) (generate-hot-nodes)))

在对该功能进行单元测试时,我想编写以下测试用例:

(deftest when-sky-blue-then-generate-sky-nodes
   (let [is-sky-blue true]
       (GenerateNodes (fn[x] println "sky nodes generated."))
          (is (= true Was-generate-hot-nodes-called?))

如何断言调用了函数 generate-sky-nodes ?或不 ?我会在 C# 或 java 中使用模拟框架,但我不知道 clojure。

4

4 回答 4

11

你已经拥有的离一个可用的功能版本不远了。我改变了一些东西,使其更符合 Clojure 的习惯。

以下假设 generate-sky-nodes 和 generate-hot-nodes 每个都返回一些值(除了它们具有的任何副作用之外,还可以这样做),即:

(defn generate-sky-nodes
   []
   (doseq [i (range 10)] (do-make-sky-node i))
   :sky-nodes)

然后,您的 generate-nodes 调整如下:

(defn generate-nodes
  [sky-blue? hot-outside? name]
  (cond
   (sky-blue? name) (generate-sky-nodes)
   (hot-outside?) (generate-hot-nodes)))

最后,测试的功能版本:

(deftest when-sky-blue-then-generate-sky-nodes
  (let [truthy (constantly true)
        falsey (constantly false)
        name nil]
  (is (= (generate-nodes truthy falsey name)
         :sky-nodes))
  (is (= (generate-nodes truthy truthy name)
         :sky-nodes))
  (is (not (= (generate-nodes falsey falsey name)
              :sky-nodes)))
  (is (not (= (generate-nodes falsey truthy name)
              :sky-nodes)))))

一般的想法是你不测试它做了什么,你测试它返回什么。然后你安排你的代码,以便(尽可能地)关于函数调用的所有事情都是它返回的内容。

generate-sky-nodes另一个建议是通过使用和generate-hot-nodes返回要执行的副作用来最小化发生副作用的地方的数量:

(defn generate-sky-nodes
   []
   (fn []
    (doseq [i (range 10)] (do-make-sky-node i))
    :sky-nodes))

并且您的电话generate-nodes如下所示:

(apply (generate-nodes blue-test hot-test name) [])

或者更简洁(尽管如果你对 Clojure 不太熟悉的话,这很奇怪):

((generate-nodes blue-test hot-test name))

(在上述测试代码中进行必要的修改后,测试也将适用于该版本)

于 2013-08-20T15:06:14.150 回答
9

您可以自己编写一个宏来模拟函数并检查函数是否被调用。或者您可以使用期望调用库。

(defn check-error [a b]
  (when (= a :bad-val)
  (log :error b)))

; This will pass
(deftest check-logging
  (with-expect-call (log [:error _])
  (check-error :bad-val "abc")))

; This will fail
(deftest check-logging-2
  (expect-call (log [:error _])
  (check-error :good-val "abc")))
于 2013-08-20T09:17:48.420 回答
4

使用Midje可检查项:

(unfinished is-sky-blue? hot-outside?)
(facts "about GenerateNodes"
  (fact "when the sky is blue then sky nodes are generated"
    (GenerateNodes is-sky-blue? hot-outside? ..name..) => ..sky-nodes..
    (provided
      (is-sky-blue? ..name..) => true
      (generate-sky-nodes)    => ..sky-nodes..
      (generate-hot-nodes)    => irrelevant :times 0)))
于 2013-12-17T22:24:24.397 回答
0

您可以使用mock-clj

(require ['mock-clj.core :as 'm])

(deftest when-sky-blue-then-generate-sky-nodes
  (m/with-mock [is-sky-blue? true
                generate-sky-nodes nil]
    (GenerateNodes (fn[x] println "sky nodes generated.") ... ...)
    (is (m/called? generate-sky-nodes))))
于 2017-01-10T05:16:55.317 回答