Unit-Test-Code mit einer Dateisystemabhängigkeit
Ich Schreibe eine Komponente, die, gegeben eine ZIP-Datei, muss:
- Entpacken Sie die Datei.
- Finden, die eine bestimmte dll unter den entpackten Dateien.
- Laden, die dll durch Reflexion und ruft eine Methode auf.
Ich würde gern unit-test dieser Komponente.
Bin ich versucht zu schreiben code, der sich direkt mit dem Datei-system:
void DoIt()
{
Zip.Unzip(theZipFile, "C:\\foo\\Unzipped");
System.IO.File myDll = File.Open("C:\\foo\\Unzipped\\SuperSecret.bar");
myDll.InvokeSomeSpecialMethod();
}
Aber Leute oft sagen, "nicht mit dem schreiben von unit-tests, die sich auf die Datei-system -, Datenbank -, Netzwerk, etc."
Wenn ich dies Schreibe, in einem unit-test, freundlichen Art und Weise, ich nehme an, es würde wie folgt Aussehen:
void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
string path = zipper.Unzip(theZipFile);
IFakeFile file = fileSystem.Open(path);
runner.Run(file);
}
Yay! Jetzt ist es getestet; ich kann das Futter im test verdoppelt (mocks) zu den DoIt-Methode. Aber zu welchem Preis? Ich habe jetzt definieren mussten 3 neue Schnittstellen machen diese überprüfbar. Und was, genau, bin ich dann testen? Ich Teste gerade, dass mein DoIt-Funktion richtig interagiert mit seinen Abhängigkeiten. Es testet nicht, dass die zip-Datei war entpackt, richtig, etc.
Fühlt es sich nicht wie Teste ich die Funktionalität nicht mehr. Es fühlt sich an wie ich bin nur die Prüfung der Klasse Wechselwirkungen.
Meine Frage ist: was ist der richtige Weg, um unit-test etwas, das ist abhängig von der Datei-system?
Bearbeiten ich mit .NET, aber das Konzept konnte sich bewerben Java-oder native-code zu.
InformationsquelleAutor der Frage Judah Himango | 2008-09-24
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es gibt wirklich nichts falsch mit diesem, es ist nur eine Frage, ob Sie nennen es einen Komponententest oder Integrationstest. Sie müssen nur sicherstellen, dass wenn Sie in Interaktion mit dem Dateisystem, es gibt keine unerwünschten Nebenwirkungen. Stellen Sie insbesondere sicher, dass Sie bereinigen nach sich selbst -- löschen Sie alle temporären Dateien, die Sie erstellt haben-und dass Sie nicht versehentlich überschreiben einer vorhandenen Datei, die zufällig den gleichen Namen wie eine temporäre Datei, die Sie verwenden. Verwenden Sie immer relative Pfade und nicht absolute Pfade.
Wäre es auch eine gute Idee, um
chdir()
in ein temporäres Verzeichnis, bevor Sie Ihren test, undchdir()
Rücken hinterher.InformationsquelleAutor der Antwort Adam Rosenfield
Haben Sie treffen den Nagel auf den Kopf. Was Sie testen möchten, ist die Logik Ihrer Methode, nicht unbedingt, ob eine true-Datei angesprochen werden können. Sie brauchen nicht zu testen (in dieser unit-test), ob eine Datei korrekt entpackt, Ihre Methode nimmt das für selbstverständlich. Die Schnittstellen sind wertvolle von selbst, weil Sie Abstraktionen, die Sie Programmieren können gegen, anstatt implizit oder explizit unter Berufung auf eine konkrete Umsetzung.
InformationsquelleAutor der Antwort andreas buykx
Ihre Frage stellt eine der schwierigsten Teile der Prüfung für die Entwickler nur immer hinein:
"Was zur Hölle soll ich testen?"
Dein Beispiel ist nicht sehr interessant, weil es einfach nur klebt einige API-Aufrufe zusammen, so, wenn Sie waren, um Schreibe einen unit test für Sie, die Sie würde am Ende nur behauptet, dass die Methoden aufgerufen wurden. Tests wie diese eng paar Ihrer Umsetzung im Detail zu testen. Das ist schlecht, denn jetzt müssen Sie zum ändern des test-jeder Zeit ändern Sie die details der Implementierung der Methode, weil die änderung die details der Implementierung bricht Ihre Prüfung(en)!
Mit schlechten tests ist tatsächlich schlimmer, als wenn Sie keine tests in allen.
In deinem Beispiel:
Während die Sie übergeben können, mocks, es gibt keine Logik in der Methode zu testen. Wenn Sie versuchen, einen unit-test für diese könnte es in etwa so Aussehen:
Herzlichen Glückwunsch, Sie im Grunde kopieren-einfügen die details der Implementierung Ihrer
DoIt()
Methode in einem test. Gerne pflegen.Wenn Sie tests schreiben, Sie möchten testen Sie die WAS und nicht die WIE.
Sehen Black-Box-Tests für mehr.
Den WAS ist der name der Methode (oder sollte es zumindest sein). Die WIE sind all die kleinen details zur Implementierung, die Leben in Ihrer Methode. Gute tests erlauben, Sie zu tauschen die WIEohne die WAS.
Denken Sie an es auf diese Weise, Fragen Sie sich:
"Wenn ich die details der Implementierung dieser Methode (ohne Veränderung des öffentlichen Auftrags) wird es brechen mein test(s)?"
Wenn die Antwort ja ist, werden Sie die Prüfung der WIE und nicht die WAS.
Zur Beantwortung Ihrer konkreten Frage über das testen von code mit Datei-system-Abhängigkeiten, lassen Sie uns sagen, Sie hätten etwas Interessantes passiert mit einer Datei, und Sie wollte speichern Sie die Base64-codierten Inhalt einer
byte[]
zu einer Datei. Sie können streams für dieses zu testen, ob der code das richtige tut, ohne vorher zu prüfen wie er es tut. Ein Beispiel könnte so etwas wie dies (in Java):Der test verwendet eine
ByteArrayOutputStream
aber in der Anwendung (Verwendung von dependency injection) die Reale StreamFactory (vielleicht namens FileStreamFactory) zurückkehren würdeFileOutputStream
ausoutputStream()
- und schreiben würde, einFile
.Was war interessant über die
write
Methode ist hier, dass es war das schreiben, die Inhalte aus Base64-codiert ist, so das ist, was wir getestet. Für IhreDoIt()
Methode, dies wäre mehr angemessen getestet mit einem Integrationstest.InformationsquelleAutor der Antwort Christopher Perry
Ich bin zurückhaltend, verschmutzen meinen code mit Typen und Konzepte, die es nur zu erleichtern, unit-testing. Sicher, wenn es macht das design besser und sauberer werden dann Super, aber ich denke, das ist oft nicht der Fall.
Meine Meinung dazu ist, dass Ihre unit-tests tun würde, so viel wie Sie können, die vielleicht nicht 100% Abdeckung. In der Tat, es ist vielleicht nur 10%. Der Punkt ist, Ihre unit-tests sollten schnell sein und keine externen Abhängigkeiten. Sie könnten Testfälle wie "diese Methode löst eine ArgumentNullException-Ausnahme, wenn Sie übergeben Sie null für diesen parameter".
Ich würde dann hinzufügen von integration-tests (auch automatisierter und wahrscheinlich mit der gleichen unit-Test-framework) , können externe Abhängigkeiten und test-Ende-zu-Ende-Szenarien wie diese.
Bei der Messung der code-coverage -, Messe ich sowohl unit-und integration-tests.
InformationsquelleAutor der Antwort Kent Boogaart
Es ist nichts falsch mit dem schlagen der Datei-system, nur halten es für eine integration-test eher als eine Einheit zu testen. Ich würde die swap-hart codierten Pfad durch einen relativen Pfad, und erstellen Sie eine Testdaten-Unterordner enthalten, die Reißverschlüsse für die unit-tests.
Wenn Ihr Integrations-tests zu lange laufen, dann trennen Sie diese heraus, damit Sie nicht so oft wie Ihre schnelle unit-tests.
Stimme ich zu, manchmal denke ich, interaction-based testing kann dazu führen, zu viel Kupplung und endet oft nicht genügend Wert. Sie wirklich wollen, um zu testen, entpacken Sie die Datei hier nicht nur überprüfen, dass Sie den Aufruf der passenden Methoden.
InformationsquelleAutor der Antwort JC.
Eine Möglichkeit wäre, zu schreiben, entpacken Sie die Methode, um InputStreams Dann die unit-test-Konstrukt wie ein InputStream aus einem byte-array mit dem ByteArrayInputStream. Der Inhalt des byte-array könnte eine Konstante in der unit-test-code.
InformationsquelleAutor der Antwort nsayer
Scheint dies eher ein integration-test, wie Sie abhängig sind von einem bestimmten detail (das Dateisystem), die sich ändern könnte, in der Theorie.
Ich würde abstrakter der code, der sich mit den OS in es ist ein eigenes Modul (Klasse, Montage, Glas, was auch immer). In Ihrem Fall, die Sie laden möchten eine bestimmte DLL, wenn gefunden, so stellen Sie eine IDllLoader-Schnittstelle und DllLoader Klasse. Haben Sie Ihre app erwerben, die DLL aus der DllLoader mit dem interface und test, .. du bist nicht verantwortlich für das entpacken code doch Recht?
InformationsquelleAutor der Antwort tap
Davon aus, dass "Datei-system-Interaktionen" sind gut getestet in der Rahmen selbst, erstellen Sie Ihre Methode für die Arbeit mit streams, und testen Sie diese. Öffnen ein FileStream und der übergabe an die Methode können Sie Links von den tests, als FileStream.Öffnen ist gut getestet durch das framework Schöpfer.
InformationsquelleAutor der Antwort Sunny Milenov
Sollten Sie keine test-Klasse zusammenspiel und die Funktion aufrufen. stattdessen sollten Sie überlegen, integration-Tests. Test das gewünschte Ergebnis und nicht die Datei laden Betrieb.
InformationsquelleAutor der Antwort Dror Helper
Für unit-test würde ich vorschlagen, dass Sie die test-Datei in Ihr Projekt(EAR-Datei oder vergleichbar), dann verwenden Sie einen relativen Pfad in die unit-tests, d.h. "../testdata/testfile".
Solange dein Projekt ist richtig exportiert/importiert als Ihre unit-test sollte funktionieren.
InformationsquelleAutor der Antwort James Anderson
Wie schon andere gesagt haben, die erste ist gut als ein integration-test. Die zweiten tests nur das, was die Funktion soll, um tatsächlich tun, das ist alles eine unit-test tun sollten.
Wie gezeigt, im zweiten Beispiel sieht ein wenig sinnlos, aber es gibt Ihnen die Möglichkeit, zu testen, wie die Funktion reagiert auf Fehler in einem der Schritte. Sie haben noch keine Fehlerprüfung im Beispiel, aber im realen system Sie haben können, und das dependency injection-würde Sie test alle Antworten auf etwaige Fehler. Dann werden die Kosten sich gelohnt haben wird.
InformationsquelleAutor der Antwort David Sykes