Ich habe mich bemüht, meinen Standardansatz für Test-treibenden .NET-Code an Ruby anzupassen.
Als Beispiel schreibe ich eine Klasse, die:
%Vor%Normalerweise für .NET würde ich mit etwas wie:
enden %Vor% In meinem Test (zuerst geschrieben) würde ich Finder und Converter mixen. Dann würde ich finder.FindFiles("*.markdown")
ausgeben, um ["file1", "file2"]
zurückzugeben und check converter.Convert("file1", targetDir)
und converter.Convert("file2", targetDir)
wurde aufgerufen.
Wo ich Schwierigkeiten habe, dies auf Ruby anzuwenden, verwendet Ruby eher Blöcke und interne Iteratoren (z. B. array.each { |x| puts x }
) und enthält Module über die Konstruktorinjektion. Ich bin mir nicht sicher, wie man in diesen Fällen Test-Code einrichtet (ohne einen vollständigen Integrationstest einzurichten), und der .NET-Ansatz erscheint einfach unglaublich un-rubinisch; Es scheint zu kämpfen, wie Ruby natürlich funktioniert.
Irgendwelche Vorschläge, wie man das auf Ruby-Art macht? Ein Beispiel für einen Ruby-Test für dieses Beispiel wäre großartig.
Sie könnten einen sehr genauen Kurstest haben, der ungefähr so aussieht:
%Vor%Ich nehme an, das würde einen anständigen Integrationstest machen, aber das ist nicht das, was ich wirklich mag, ich mag meinen Code in mundgerechten Stücken.
Zuerst würde ich eine Klasse erstellen, die mit der Analyse einer Markdown-Datei umgehen kann, zum Beispiel:
%Vor%Jetzt befindet sich der gesamte Code zum Lesen und Parsen von Markdown-Dateien in der MarkdownReader-Klasse. Jetzt, wenn wir nicht wirklich Dateien schreiben wollen, können Sie sich etwas einfallen lassen und mit RR oder Mocha oder etwas spotten (ich benutze hier rr):
%Vor%Wenn Sie nun davon ausgehen, dass die ExamplesToCode-Klasse die Klassen MarkdownReader und CodeSampleWriter verwendet, können Sie wieder Mock-Objekte mit RR wie folgt verwenden:
%Vor%Hoffentlich gibt Ihnen das ein paar Ideen, wie Sie in Ruby testen können. Nicht inflammatorisch, aber in den meisten Fällen ist die Abhängigkeitsinjektion nicht etwas, das in der Ruby-Welt gesehen oder benutzt wird - es fügt normalerweise viel Overhead hinzu. Mocking / Doubles sind im Allgemeinen eine viel bessere Option zum Testen.
Bevor ich die Frage über die Vorgehensweise in Ruby beantworte, möchte ich einige Missverständnisse klären.
Erstens würde ich nicht sagen, dass es einen "Ruby-Weg" gibt, Dinge wie diese zu testen, als es eine strenge Methode gibt, so etwas in .NET zu testen (was ich zugegebenermaßen seit Jahren nicht mehr verwendet habe). Man könnte einen interaktionsbasierten (spöttischen) Ansatz wählen oder, wie Sie sagten, einen eher staatsbasierten Ansatz verwenden, indem Sie einen Integrationstest erstellen, der alle drei Klassen gleichzeitig trainiert. Die Kompromisse zwischen den beiden Ansätzen sind meiner Meinung nach sprachunabhängig. Ruby hat viele mocking Frameworks , mit denen Sie eine Interaktion durchführen können wenn Sie sich am wohlsten fühlen. (Ich verwende normalerweise das, das mit RSpec geliefert wird.)
Zweitens glaube ich nicht, dass "Module über Konstruktorinjektion einschließen" eine genaue Aussage ist. Module sind ein zusätzliches Werkzeug, das Ihnen in Ruby zur Verfügung steht, ersetzen aber keinesfalls gutes OO-Design durch Objektkomposition. Ich gebe die Abhängigkeiten zu meinen Initialisierern die ganze Zeit in Ruby ein, da es sie einfacher zu testen und wiederverwendbar macht. Ich werde im Allgemeinen die Abhängigkeit in der Argumentliste jedoch wie so def initialize(converter=CodeConverter.new)
vorgeben.
Nun, um Ihre Frage zu beantworten. Was limmyclennan über die Verwendung von Dir::[]
gesagt hat, ist korrekt - das finder
wird nicht benötigt. Die Frage ist also, wie schreibt man Tests für Methoden, die Dir::[]
aufrufen? Sie haben drei Möglichkeiten: 1) Verwenden Sie eine der oben genannten Mocking-Bibliotheken und Stub Dir::[]
(Dies ist eine einfache und einfache Methode), 2) Schreiben Sie die Dateien auf die Festplatte und überprüfen Sie sie gelesen werden (ick), oder 3) verwenden eine Bibliothek wie FakeFS , um Festplatten-IO zu verhindern, aber trotzdem einen natürlich aussehenden Test schreiben zu können. Das folgende Beispiel ist ein möglicher Weg, dies zu schreiben / zu testen (mit RSpec und FakeFS), der etwas parallel zu Ihrem ursprünglichen Design ist:
Natürlich stellt sich die Frage, ob ein solcher Test genug Wert hinzufügt, um seine Existenz zu rechtfertigen. Ich denke aber nicht, dass ich mich darauf besinnen werde. Wenn Sie sich dafür entscheiden, FakeFS zu verwenden, sollten Sie beachten, dass der Stacktraces von Fehlern nicht hilfreich sein kann, da der FS gefälscht wird, wenn RSpec versucht, die Zeilennummer zu entfernen die nicht existente FS. :) Zufälligerweise habe ich einen Code, der Abschriften-Slides auf GitHub liest und analysiert. Die Spezifikationen können als weitere Informationen dienen. Beispiele , wie Sie solche Dinge in Ruby testen können. HTH, und viel Glück.
Aus all dem Pseudocode, das einzige, was mich wirklich beunruhigt, ist "extrahiert Code-Beispiele aus der Datei". Lesen von Dateien aus einem Verzeichnis ist trivial, Speichern einer Datei ist trivial. Ungeachtet des Test-Frameworks würde ich mich die meiste Zeit mit dem Parsen beschäftigen.
Beim direkten Testen würde ich die Snippets direkt in den Testfall einbetten:
%Vor%Ah, ich sehe eine weitere Änderung, die ich subtil während des Schreibens des Tests gemacht habe: my ExamplesToCode.parse () gibt ein Array (oder einen anderen iterierbaren Container) zurück, so dass es abgesehen von der Iteration selbst getestet werden kann.
Auch in Ruby gibt es nur zwei Möglichkeiten, diesen Code zu entkoppeln: DI oder einen Service Locator. Von den beiden bevorzuge ich immer noch DI, wie du es beschrieben hast.
Ich bin mir der Ruby-Idiome nicht sicher, aber ich vermute, dass sie sich nicht mit der IFileFinder-Abstraktion beschäftigen würden, sondern direkt Dir ["* .makrkdown"] aufrufen und dann im Test neu schreiben.
Interessanterweise hat Derick Bailey von LosTechies.com gerade einen Blogeintrag über Formcode veröffentlicht, um leichter testbar zu sein:
Derick erwähnt, dass Sie in Ruby nicht so hart wie andere Sprachen wie C # versucht haben, dass Ihr Code testbar ist.
Vielleicht ist die Antwort, dass Ihr Top-Down-BDD-artiger Workflow, den Sie von JP Boodhoos Nothing But .NET Bootcamp übernommen haben Ссылка trifft nicht auf die gleiche Weise zu wie in C #. Gleiches würde für meinen kleinen Anagramm-Code-Kata-Versuch gehen, den ich auf meinem Blog eine Anzahl von Monaten zurück machte, wo ich ähnliche Techniken in C # erkundete Ссылка . Ich versuche herauszufinden, was das bedeuten würde ... vielleicht müssen Sie die Idee der Interfaces verwerfen, denn in Ruby sollten Sie Polymorphismus über Komposition und nicht Vererbung machen.