你已经离工作的功能版本不远了。我改变了一些东西,使其更加习惯于Clojure。
以下假设产生天空节点,并产生热节点都返回一定的价值(这可以在除任何副作用他们完成),即:
(defn generate-sky-nodes
[]
(doseq [i (range 10)] (do-make-sky-node i))
:sky-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))
(在上面的测试代码比照测试将使用该版本的工作以及)
好问题。在我看来,你正试图对声明性代码应用命令式的风格测试。你不应该描述*事情是如何工作的,而是他们做了什么,所以被调用的函数是一个不相关的细节。不过功能编程专家会确认(我不知道)。 – guillaume31
@ guillaume31,我认为这是模拟和存根之间的区别。存根只是为了提供支持行为的假实现,而嘲讽也是这样做的,并且还会计帐。就我个人而言,我发现嘲笑是非常糟糕的主意,即使在面向对象的世界。在功能世界中是双重的。但它可能只是我。 – ivant
@ivant Dunno。我猜测存根仍会以某种方式描述* how *,尽管如果没有他们可能无法获得高性能测试。嘲笑我个人觉得有用,不是为了微观会计,而是为了验证一个对象不会向其中的一个对等方说粗鲁(即外部协议),这使得在OO类型系统中缺乏这些协议的流畅执行。 – guillaume31