Quello che hai già non è lontano da una versione funzionante. Ho cambiato le cose un po 'per essere più idiomatico di Clojure.
Di seguito si presuppone che generano-sky-nodi e generare-hot-nodi ogni ritorno certo valore (questo può essere fatto in aggiunta a eventuali effetti collaterali che hanno), vale a dire:
(defn generate-sky-nodes
[]
(doseq [i (range 10)] (do-make-sky-node i))
:sky-nodes)
allora, la tua generare nodi viene regolata come segue:
(defn generate-nodes
[sky-blue? hot-outside? name]
(cond
(sky-blue? name) (generate-sky-nodes)
(hot-outside?) (generate-hot-nodes)))
e, infine, la versione funzionale delle prove:
(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)))))
L'idea generale è che non si prova ciò che ha fatto, si prova ciò che restituisce. Quindi disponi il tuo codice in modo tale che (quando possibile) tutto ciò che conta su una chiamata di funzione è ciò che restituisce.
Un ulteriore suggerimento è quello di minimizzare il numero di luoghi in cui avvengono gli effetti collaterali utilizzando generate-sky-nodes
e generate-hot-nodes
di restituire l'effetto collaterale da effettuare:
(defn generate-sky-nodes
[]
(fn []
(doseq [i (range 10)] (do-make-sky-node i))
:sky-nodes))
e la chiamata di generate-nodes
sarà simile alla seguente :
(apply (generate-nodes blue-test hot-test name) [])
o più brevemente (anche se certamente strano se si hanno meno familiarità con Clojure):
((generate-nodes blue-test hot-test name))
(mutatis mutandis, nel codice di prova sopra i test funzionano con questa versione così)
Buona domanda. Mi sembra che tu stia provando ad applicare un imperativo test di stile al codice dichiarativo.Non dovresti descrivere * come * le cose funzionano, ma * cosa * fanno, quindi una funzione che viene chiamata è IMO un dettaglio irrilevante. Per essere confermato dagli esperti di programmazione funzionale però (che io non sono). – guillaume31
@ guillaume31, penso che questa sia la differenza tra mock e stub. Gli stub servono solo a fornire false implementazioni del comportamento di supporto, mentre i mock lo fanno e fanno anche contabilità. Personalmente trovo che i mock siano eccezionalmente cattivi, anche nel mondo OO. Doubly so in functional world. Ma potrebbe essere solo io. – ivant
@ivant Dunno. Credo che gli stub descrivono ancora in qualche modo * come *, anche se probabilmente non puoi farne a meno per ottenere test performanti. Mazze Personalmente trovo utile, non per fare micro-contabilità, ma per verificare che un oggetto non parli maleducato (cioè il protocollo esterno) a uno dei suoi colleghi, rendendo la mancanza di un'applicazione fluente di questi protocolli nei sistemi di tipi OO. – guillaume31