Ich habe einige Klassen, die eine Logik im Zusammenhang mit Dateisystemen und Dateien implementieren. Im Rahmen dieser Logik führe ich beispielsweise folgende Aufgaben aus:
Jetzt hat all diese Logik einen gewissen Workflow und es werden Ausnahmen geworfen, wenn etwas nicht stimmt (z. B. Konfigurationsdatei wird nicht am spezifischen Ordnerort gefunden). Darüber hinaus ist Managed Extensibility Framework (MEF) an dieser Logik beteiligt, da einige dieser Dateien, die ich überprüfe, verwaltete DLLs sind, die ich manuell in MEF-Aggregate usw. lade.
Jetzt möchte ich das alles auf irgendeine Weise testen. Ich dachte daran, mehrere physische Testordner auf der Festplatte zu erstellen, die verschiedene Testfälle abdecken, und dann meinen Code für sie auszuführen. Ich könnte zum Beispiel erstellen:
Wäre das der richtige Ansatz? Ich bin mir jedoch nicht sicher, wie ich meinen Code in diesem Szenario genau ausführen soll. Ich möchte auf keinen Fall die gesamte Anwendung ausführen und darauf verweisen, um diese verspotteten Ordner zu überprüfen. Sollte ich ein Unit-Test-Framework verwenden, um Art von "Unit-Tests" zu schreiben, die meinen Code für diese Dateisystemobjekte ausführen?
Ist all dies im Allgemeinen ein korrekter Ansatz für diese Art von Testszenarien? Gibt es andere bessere Ansätze?
Zuallererst, denke ich, ist es besser, Komponententests zu schreiben, um Ihre Logik zu testen, ohne externe Ressourcen zu berühren. Hier haben Sie zwei Möglichkeiten:
In Unit-Tests müssen Sie die Logik externer Bibliotheken wie MEF nicht testen.
Zweitens, wenn Sie Integrationstests schreiben möchten, müssen Sie einen "Happy Path" -Test (wenn alles in Ordnung ist) und einige Tests schreiben, die Ihre Logik in Grenzfällen testen (Datei oder Verzeichnis nicht gefunden). Im Gegensatz zu @Sergey Berezovskiy empfehle ich, separate Ordner für jeden Testfall zu erstellen. Die Hauptvorteile sind:
Für Unit- und Integrationstests Sie können normale Unit-Test-Frameworks verwenden (wie NUnit oder xUnit.NET). Mit diesem Framework ist es ziemlich einfach, Ihre Tests in Continuous-Integrationsszenarien auf Ihrem Build-Server zu starten.
Wenn Sie sich entscheiden, beide Arten von Tests zu schreiben, dann Sie müssen Unit-Tests von Integrationstests trennen (Sie können für jede Art von Tests separate Projekte erstellen). Gründe dafür:
Sie sollten mit Unit-Tests so viel Logik wie möglich testen, indem Sie Aufrufe an das Dateisystem hinter Schnittstellen abstrahieren. Durch die Verwendung von Dependency Injection und einem Testframework wie FakeItEasy können Sie testen, ob Ihre Schnittstellen tatsächlich zur Bearbeitung der Dateien und Ordner verwendet/aufgerufen werden.
Irgendwann müssen Sie jedoch die Implementierungen testen, die auch auf dem Dateisystem funktionieren, und hier müssen Sie Integrationstests durchführen.
Die Dinge, die Sie testen müssen, scheinen relativ isoliert zu sein, da Sie nur Ihre eigenen Dateien und Verzeichnisse auf Ihrem eigenen Dateisystem testen möchten. Wenn Sie eine Datenbank oder ein anderes externes System mit mehreren Benutzern usw. testen möchten, sind die Dinge möglicherweise komplizierter.
Ich glaube nicht, dass es "offizielle Regeln" dafür gibt, wie man solche Integrationstests am besten durchführt, aber ich glaube, Sie sind auf dem richtigen Weg. Einige Ideen, auf die Sie hinarbeiten sollten:
In Ihrer Situation würde ich zwei Hauptordner einrichten: Einen, in dem alles so ist, wie es sein soll (d. H. Richtig funktioniert), und einen, in dem alle Regeln verletzt sind.
Ich würde diese Ordner und alle Dateien in ihnen erstellen, dann jeden der Ordner komprimieren und Logik in eine Testklasse schreiben, um jeden von ihnen zu dekomprimieren.
Dies sind keine wirklichen Tests; Stellen Sie sich diese stattdessen als "Skripte" zum Einrichten Ihres Testszenarios vor, mit denen Sie Ihre Ordner und Dateien einfach und schnell löschen und neu erstellen können, selbst wenn Ihre Hauptintegrationstests sie während des Tests ändern oder durcheinander bringen sollten. Der Grund, warum Sie sie in eine Testklasse einordnen, besteht darin, dass Sie sie einfach über dieselbe Schnittstelle ausführen können, mit der Sie während des Tests arbeiten werden.
Erstellen Sie zwei Sätze von Testklassen, einen für jede Situation (Richten Sie Ordner gegen Ordner mit fehlerhaften Regeln richtig ein). Platzieren Sie diese Tests in einer Ordnerhierarchie, die für Sie von Bedeutung ist (abhängig von der Komplexität Ihrer Situation).
Es ist nicht klar, wie gut Sie mit Unit-/Integrationstests vertraut sind. In jedem Fall würde ich NUnit empfehlen. Ich verwende die Erweiterungen auch gerne in Should
. Beides erhalten Sie bei Nuget:
install-package Nunit
install-package Should
Mit dem Paket should können Sie den Testcode folgendermaßen schreiben:
someCalculatedIntValue.ShouldEqual(3);
someFoundBoolValue.ShouldBeTrue();
Beachten Sie, dass mehrere Testläufer verfügbar sind, mit denen Sie Ihre Tests durchführen können. Ich persönlich habe nur echte Erfahrungen mit dem in Resharper eingebauten Läufer gemacht, bin aber sehr zufrieden und kann ihn ohne Probleme weiterempfehlen.
Unten sehen Sie ein Beispiel einer einfachen Testklasse mit zwei Tests. Beachten Sie, dass wir im ersten Fall mit einer Erweiterungsmethode von Should nach einem erwarteten Wert suchen, während wir im zweiten Fall nichts explizit testen. Dies liegt daran, dass es mit [ExpectedException] markiert ist, was bedeutet, dass es fehlschlägt, wenn beim Ausführen des Tests keine Ausnahme des angegebenen Typs ausgelöst wird. Sie können dies verwenden, um zu überprüfen, ob eine entsprechende Ausnahme ausgelöst wird, wenn eine Ihrer Regeln verletzt wird.
[TestFixture]
public class When_calculating_sums
{
private MyCalculator _calc;
private int _result;
[SetUp] // Runs before each test
public void SetUp()
{
// Create an instance of the class to test:
_calc = new MyCalculator();
// Logic to test the result of:
_result = _calc.Add(1, 1);
}
[Test] // First test
public void Should_return_correct_sum()
{
_result.ShouldEqual(2);
}
[Test] // Second test
[ExpectedException(typeof (DivideByZeroException))]
public void Should_throw_exception_for_invalid_values()
{
// Divide by 0 should throw a DivideByZeroException:
var otherResult = _calc.Divide(5, 0);
}
[TearDown] // Runs after each test (seldom needed in practice)
public void TearDown()
{
_calc.Dispose();
}
}
Mit all dem sollten Sie in der Lage sein, Testszenarien zu erstellen, neu zu erstellen und Tests auf einfache und wiederholbare Weise durchzuführen.
Bearbeiten: Wie in einem Kommentar ausgeführt, ist Assert.Throws () eine weitere Option , um sicherzustellen, dass Ausnahmen wie erforderlich ausgelöst werden. Persönlich mag ich die Tag-Variante, und mit Parametern , Sie können dort auch Dinge wie die Fehlermeldung überprüfen. Ein weiteres Beispiel (unter der Annahme, dass eine benutzerdefinierte Fehlermeldung von Ihrem Rechner ausgegeben wird):
[ExpectedException(typeof(DivideByZeroException), ExpectedMessage="Attempted to divide by zero" )]
public void When_attempting_something_silly(){
...
}
Ich würde mit einem einzigen Testordner gehen. Für verschiedene Testfälle können Sie im Rahmen der Kontexteinstellung verschiedene gültige/ungültige Dateien in diesen Ordner kopieren. Im Test-Teardown einfach diese Dateien aus dem Ordner entfernen.
Z.B. mit Specflow :
Given configuration file not exist
When something
Then foo
Given configuration file exists
And some dll not exists
When something
Then bar
Definieren Sie jeden Schritt zum Einrichten des Kontexts als Kopieren/Nicht-Kopieren der entsprechenden Datei in Ihren Ordner. Sie können auch Tabelle verwenden, um festzulegen, welche Datei in den Ordner kopiert werden soll:
Given some scenario
| FileName |
| a.config |
| b.invalid.config |
When something
Then foobar
Ich kenne die Architektur Ihres Programms nicht, um einen guten Rat zu geben, aber ich werde es versuchen
Ich würde Framework-Logik erstellen und Parallelitätsprobleme und Dateisystemausnahmen testen, um eine gut definierte Testumgebung sicherzustellen.
Versuchen Sie, alle Grenzen der Problemdomäne aufzulisten. Wenn es zu viele gibt, sollten Sie die Möglichkeit in Betracht ziehen, dass Ihr Problem zu weit gefasst ist und aufgeschlüsselt werden muss. Was sind die vollständigen erforderlichen und ausreichenden Bedingungen, damit Ihr System alle Tests besteht? Betrachten Sie dann jeden Zustand und behandeln Sie ihn als individuellen Angriffspunkt. Und liste alle Möglichkeiten auf, die dir einfallen, wenn du dagegen verstößt. Versuchen Sie, sich selbst zu beweisen, dass Sie alle gefunden haben. Dann schreiben Sie jeweils einen Test.
Ich würde den obigen Prozess zuerst für die Umgebung durchgehen, diesen erst zu einem zufriedenstellenden Standard erstellen und testen und dann für die detailliertere Logik innerhalb des Workflows. Möglicherweise ist eine gewisse Iteration erforderlich, wenn beim Testen Abhängigkeiten zwischen der Umgebung und der detaillierten Logik auftreten.