2013-03-29 6 views
13

Ich erinnere mich, einen Artikel darüber gelesen zu haben, wie Ruby DI- oder DI-Frameworks nicht wirklich braucht, weil die Klassen offen sind. Daher können Sie den Konstruktor einer Abhängigkeit einfach neu schreiben, sodass ein falsches Objekt zurückgegeben wird.Braucht clojure eine Abhängigkeitsinjektion, um Code testbarer zu machen?

Ich bin sehr neu zu Clojure und funktionale Programmierung. Ich frage mich, ob Clojure Abhängigkeitsinjektion benötigt oder es aus ähnlichen/anderen Gründen darauf verzichten kann. Hier ist ein konkretes Beispiel, mit dem man arbeiten kann (Fühlen Sie sich frei, wie Sie darauf hinweisen, wie mein Design von Clojure nicht idiomatisch ist):

Stellen Sie sich vor, Sie entwickeln einen Web Crawler/Spider. Es muss eine Webseite durchlaufen, die Sie heruntergeladen haben. Dies ist eine Aktion mit Nebenwirkungen. Die Webseite könnte sich bei jeder Abfrage ändern, Ihre Internetverbindung könnte unterbrochen werden usw. Sie findet alle Links auf der Webseite, besucht sie und durchläuft sie auf die gleiche Weise.

Jetzt möchten Sie einen Test schreiben, der den HTTP-Client ausspioniert, so dass er stattdessen eine hartcodierte String-Antwort zurückgibt. Wie rufen Sie das Programm -main in einem Test und verhindern, dass es den echten HTTP-Client verwendet?

+0

Siehe: http://stackoverflow.com/questions/13085370/what-is-the-clojure-equivalent-to-google-guice – noahlz

Antwort

15

Das with-redefs Makro in clojure.core ist sehr nützlich zum Stub-out Funktionen.

Hier eine kurze REPL Sitzung dies zu demonstrieren:

user=> (defn crawl [url] 
    #_=> ;; would now make http connection and download content 
    #_=> "data from the cloud") 
#'user/crawl 
user=> (defn -main [& args] 
    #_=> (crawl "http://www.google.com")) 
#'user/-main 
user=> (-main) 
"data from the cloud" 
user=> (with-redefs [crawl (fn [url] "fake data")] 
    #_=> (-main)) 
"fake data" 

Da ein Clojure-Programm besteht (meist) von Funktionen, nicht Objekten, dynamischer Rebinding von Funktionen ersetzt viel von dem, was ein Rahmen DI tun würde zu Testzwecken.

10

In Clojure Sie in der Regel das Äquivalent von Dependency Injection mit alternativen Methoden erreicht werden:

  • Dynamische Bindung - z.B. nützlich für indem (binding [*out* some-writer-object] ...)
  • Funktionen höherer Ordnung Standardausgabe in Testfunktionen Umleiten - verwendet werden kann als
  • Konfiguration mit Datenparameter, indem andere Funktionen eine Form von Dependency Injection zu schaffen - es ist ziemlich idiomatische in Clojure um Karten herumzugeben, die Konfigurationsparameter enthalten (oder sogar Funktionen, um benutzerdefiniertes Verhalten zu konfigurieren).

Alle diese sind integraler Bestandteil der Sprache selbst. Du brauchst definitiv nichts wie ein "DI-Framework". IMHO, die einen Rahmen für DI benötigt, ist wirklich nur ein Ausgleich für einen Mangel an ausreichenden Features in der Sprache selbst.

2

Stellen Sie sich vor, Sie entwickeln einen Web-Crawler/Spider. Es muss eine Webseite durchlaufen, die Sie heruntergeladen haben. Dies ist eine Aktion mit Nebenwirkungen. Die Webseite könnte sich bei jeder Abfrage ändern, Ihre Internetverbindung könnte ausschneiden, usw. Sie findet alle Links auf der Webseite, besucht jede einzelne, und traversiert sie dann auf die gleiche Weise.

Ich sehe hier keine Notwendigkeit für Abhängigkeitsinjektion.Hier ist, wie ich denke, ich würde dieses Problem angehen, während die Testbarkeit beibehalten: die Implementierung in ein Minimum von zwei Funktionen trennen. Eine Funktion ruft die Webseite ab und gibt sie zurück, und die andere analysiert die Antwort. In Ihrer Hauptfunktion würden Sie etwas wie (-> "http://google.com" fetch parse) haben.

Dann, um parse zu testen, können Sie einfach die fetch Methode umgehen und falsche Webseite Daten direkt in die parse Methode füttern.

(deftest test 
    (is (= {:something "blah"} (parse "<html><head><title>Fake webpage etc..</title></head></html>")))) 

So lange, wie Sie über den Abbau Probleme in klare Funktionen aufpasst, ich glaube nicht, dass Sie anspruchsvolle etwas brauchen wie DI zu testen.

Ich bin neu in Clojure, also bin ich nicht vertraut mit den Makros und Techniken, die hier von anderen erwähnt werden, aber die obige Methodik hat mir bisher gut getan.

+1

Das ist ein guter Rat und ich denke, es ist etwas, was ich tun würde, aber ich denke, das ist mehr von einem Unit-Test. Manchmal benötigen Sie einen Abnahmetest, der mehr als nur ein Teil prüft. –

Verwandte Themen