Testen mit all den Abhängigkeiten ist immer noch wichtig, aber es geht mehr um Integrationstests, wie Jeffrey Faust sagte.
Einer der wichtigsten Aspekte von Unit-Tests ist es, Ihre Tests vertrauenswürdig zu machen. Wenn Sie nicht glauben, dass ein bestandener Test wirklich bedeutet, dass die Dinge gut sind und dass ein nicht bestandener Test ein Problem im Produktionscode darstellt, sind Ihre Tests bei weitem nicht so nützlich, wie sie sein können.
Um Ihre Tests vertrauenswürdig zu machen, müssen Sie einige Dinge tun, aber ich werde mich für diese Antwort auf eine konzentrieren. Sie müssen sicherstellen, dass sie einfach auszuführen sind, damit alle Entwickler sie problemlos ausführen können, bevor sie den Code einchecken. "Einfach auszuführen" bedeutet, dass Ihre Tests schnell ausgeführt werden und keine umfangreiche Konfiguration oder Einrichtung erforderlich ist, um sie zum Laufen zu bringen. Im Idealfall sollte jeder in der Lage sein, die neueste Version des Codes zu überprüfen, die Tests sofort auszuführen und zu sehen, ob sie bestanden wurden.
Durch das Abstrahieren von Abhängigkeiten von anderen Dingen (Dateisystem, Datenbank, Webservices usw.) können Sie die erforderliche Konfiguration vermeiden und sind weniger anfällig für Situationen, in denen Sie versucht sind, "Oh, Tests sind fehlgeschlagen, weil ich nicht" zu sagen. Ich habe die Netzwerkfreigabe nicht eingerichtet. Na ja. Ich werde sie später ausführen. "
Wenn Sie testen möchten, was Sie mit einigen Daten tun, sollten sich Ihre Unit-Tests für diesen Geschäftslogikcode nicht darum kümmern, wie Sie diese Daten erhalten. Es ist fantastisch, die Kernlogik Ihrer Anwendung testen zu können, ohne auf die Unterstützung von Dingen wie Datenbanken angewiesen zu sein. Wenn Sie es nicht tun, verpassen Sie es.
PS Ich sollte hinzufügen, dass es definitiv möglich ist, im Namen der Testbarkeit zu überdenken. Das Testen Ihrer Anwendung trägt dazu bei, dies zu vermeiden. In jedem Fall machen schlechte Implementierungen eines Ansatzes den Ansatz jedoch nicht weniger gültig. Alles kann missbraucht und übertrieben werden, wenn man nicht immer fragt: "Warum mache ich das?" während der Entwicklung.
In Bezug auf interne Abhängigkeiten wird es etwas matschig. Ich denke gerne darüber nach, dass ich meine Klasse so gut wie möglich davor schützen möchte, sich aus den falschen Gründen zu ändern. Wenn ich ein Setup wie dieses habe ...
public class MyClass
{
private SomeClass someClass;
public MyClass()
{
someClass = new SomeClass();
}
// use someClass in some way
}
Es ist mir im Allgemeinen egal, wie SomeClass
erstellt wird. Ich möchte es nur benutzen. Wenn SomeClass sich ändert und nun Parameter für den Konstruktor benötigt, ist das nicht mein Problem. Ich sollte MyClass nicht ändern müssen, um das zu berücksichtigen.
Nun, das berührt nur den Designteil. Bei Unit-Tests möchte ich mich auch vor anderen Klassen schützen. Wenn ich MyClass teste, mag ich es zu wissen, dass es keine externen Abhängigkeiten gibt, dass SomeClass zu irgendeinem Zeitpunkt keine Datenbankverbindung oder keinen anderen externen Link eingeführt hat.
Ein noch größeres Problem ist, dass ich auch weiß, dass einige Ergebnisse meiner Methoden von der Ausgabe einer Methode in SomeClass abhängen. Ohne SomeClass zu verspotten / auszublenden, habe ich möglicherweise keine Möglichkeit, diesen Input in der Nachfrage zu variieren. Wenn ich Glück habe, kann ich meine Umgebung im Test möglicherweise so zusammenstellen, dass die richtige Antwort von SomeClass ausgelöst wird. Auf diese Weise werden meine Tests jedoch komplexer und spröder.
Wenn ich MyClass so umschreibe, dass eine Instanz von SomeClass im Konstruktor akzeptiert wird, kann ich eine gefälschte Instanz von SomeClass erstellen, die den gewünschten Wert zurückgibt (entweder über ein spöttisches Framework oder mit einem manuellen Mock). In diesem Fall muss ich in der Regel keine Schnittstelle einführen. Ob Sie dies tun oder nicht, ist in vielerlei Hinsicht eine persönliche Entscheidung, die von der Sprache Ihrer Wahl bestimmt wird (z. B. sind Benutzeroberflächen in C # wahrscheinlicher, aber in Ruby benötigen Sie definitiv keine).