Что у вас уже есть недалеко от рабочей функциональной версии. Я немного изменил ситуацию, чтобы быть более идиоматичным для 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))
(с учетом соответствующих изменений в приведенном выше тестовом коде тесты будут работать с этой версией, а)
Хороший вопрос. Мне кажется, вы пытаетесь применить обязательное тестирование стиля к декларативному коду.Вы не должны описывать *, как все работает, но * что * они делают, поэтому вызываемая функция является IMO неуместной деталью. Подтверждаются эксперты по функциональному программированию (что я не знаю). – guillaume31
@ guillaume31, я думаю, что это разница между mocks и stub. Заготовки там только для того, чтобы обеспечить поддельные реализации поддерживающего поведения, в то время как макеты делают это, а также делают учет. Лично я считаю, что издевательства - исключительно плохая идея, даже в мире ОО. Совсем так в функциональном мире. Но это может быть только я. – ivant
@ivant Не знаю. Я думаю, что заглушки все еще каким-то образом описывают * как *, хотя вы, вероятно, не можете обойтись без них, чтобы получить тесты на выполнение. Моски, которые я лично считаю полезными, а не для микро-учета, но для проверки того, что объект не говорит грубо (т. Е. Внешний протокол) с одним из своих сверстников, делая из-за отсутствия свободного применения этих протоколов в системах типов OO. – guillaume31