Ist die Abhängigkeitsinjektion für Komponententests unerlässlich?

55

Ist die Verwendung von Dependency Injection (DI) für Unit-Tests unerlässlich?

Ich kann mir keine andere Alternative zum Isolieren von Code vorstellen, damit dieser getestet werden kann. Auch alle Beispiele, die ich je gesehen habe, verwenden dieses Muster. Liegt das daran, dass dies die einzig gangbare Option ist, oder gibt es andere Alternativen?

Tom Squires
quelle
5
Abhängigkeitsinjektion ist nicht wesentlich, aber das umfassendere Konzept der Inversion der Kontrolle ist es.
Jeremy Heiler
Für die Skala ist hier etwas zu sagen. Wenn ich eine kleine Codebasis mit sehr wenigen Ebenen habe, ist DI möglicherweise nicht nützlich.
JB King
@ JBKing, wenn Sie eine kleine Codebasis haben, brauchen Sie keine Ebenen oder Unit-Tests
Sklivvz
1
Viele Designentscheidungen sind notwendig, um Ihren Code testbar zu machen. Schreibe Tests und finde es heraus.
Nathan Cooper

Antworten:

42

DI erleichtert das Testen von Einheiten erheblich. Sie können jedoch weiterhin Komponententests ohne DI schreiben. Viele Unit-Tests wurden bereits geschrieben, bevor sich DI verbreitete. (Natürlich sind einige dieser verwendeten Techniken identisch oder sehr ähnlich zu DI, ohne zu wissen, dass sie einen ausgefallenen Namen haben :-)

Ich selbst habe zB Schnittstellen und Fabriken viel benutzt, bevor ich etwas über DI gelernt habe. Der tatsächliche Name der Factory-Klasse wurde möglicherweise aus einer Konfigurationsdatei gelesen oder als Argument an das SUT übergeben.

Ein anderer Ansatz ist die Verwendung von Singletons (oder allgemein global zugänglichen Daten). Ja, ich weiß, dass es von vielen (einschließlich mir selbst) im Allgemeinen nicht empfohlen wird. Dennoch kann es in bestimmten Situationen sinnvoll sein, insbesondere wenn der Singleton statische Konfigurationsdaten enthält, die nicht testfallspezifisch sind, sich jedoch zwischen Produktions- und Testumgebung unterscheiden. Natürlich hat es seine bekannten Probleme, also ist DI überlegen, wenn Sie es verwenden können. Aber oft (z. B. in Legacy-Systemen) können Sie nicht.

Apropos: Effektiv mit Legacy-Code arbeiten beschreibt viele Tricks, um Legacy-Code in Tests zu erfassen. Viele davon sind nicht nett und nicht als langfristige Lösung gedacht. Sie ermöglichen es Ihnen jedoch, die ersten wertvollen Komponententests für ein ansonsten nicht testbares System zu erstellen. So können Sie mit dem Refactoring beginnen und schließlich (unter anderem) DI einführen.

Péter Török
quelle
Einverstanden ist DI besonders nützlich, um Objekte zu verspotten. Es gibt jedoch viele Szenarien, in denen DI in Tests unbrauchbar ist.
Kemoda
@Kemoda wie zum Beispiel was?
BЈови12
@VJovic Zum Beispiel eigenständige Objekte. Ich denke an eine Methode, die einige Argumente übernimmt und einige Dinge ausführt, aber nicht von einer anderen Komponente abhängt (daher wird DI nicht benötigt).
Kemoda
@Kemoda Es hört sich so an, als würden Sie die funktionale Programmierung beschreiben und DI verwenden. Sie injizieren Ihre Abhängigkeiten als Methodenparameter.
Erik Dietrich
3
@huggie, warum würden hier Implementierungsdetails durchgesickert? Die injizierte Abhängigkeit ist normalerweise hinter einer Schnittstelle verborgen, und der springende Punkt ist, dass die Client-Klasse keine Ahnung hat - und sich nicht darum kümmert -, ob die tatsächliche Implementierung dieser Abhängigkeit eine echte Produktionsklasse oder ein Schein ist. Die Instanziierung erfolgt außerhalb der Client-Klasse, es wird nur die fertige Instanz angezeigt.
Péter Török
56

Die Entkopplung ist für Unit-Tests unerlässlich. DI ist ein guter Weg, um eine Entkopplung zu erreichen.

Nlawalker
quelle
17
Eine sehr zutreffende Aussage, die die Frage in keiner Weise beantwortet.
Travis
10

Abhängig von den von Ihnen verwendeten Technologien können Sie Abhängigkeiten ohne DI isolieren. In der .NET-Welt können Sie mit Moles beispielsweise Abhängigkeiten ohne das DI-Muster isolieren.

Ich glaube jedoch, dass diese Isolations-Frameworks für Situationen in Ihrem Code mit externen Abhängigkeiten (Dateisystem, Datenbank usw.) geschrieben und vorgesehen sind. Das heißt, die Tatsache, dass man das kann, bedeutet nicht, dass er oder sie es sollte.

Die Abhängigkeitsinjektion ermöglicht das Testen von Einheiten, ermöglicht jedoch auch das Ändern des Verhaltens eines Objekts, ohne den Code dieses Objekts zu ändern (offenes / geschlossenes Prinzip). Es entsteht also nicht nur testbarer Code, sondern flexibler Code. Ich habe im Allgemeinen festgestellt, dass es eine starke Korrelation zwischen wartbarem / flexiblem Code und testbarem Code gibt.

Erik Dietrich
quelle
3
Oder Moles lässt Sie glauben, Sie hätten sauberen, gut faktorisierten, testbaren Code, weil Sie mit Magie testen.
Wyatt Barnett
@WyattBarnett Ja, sehr wahr. Maulwürfe haben eine zusammenzuckende Fähigkeit, jemanden dazu zu bringen, zu sagen: "Wer braucht all diesen Polymorphismus und all das offene / geschlossene Zeug überhaupt?!?"
Erik Dietrich
1
Maulwürfe wurden durch Fälschungen ersetzt und von der Seite "Fälschungen" aus "Das Fakes-Framework hilft Entwicklern, in ihren Komponententests Dummy-Implementierungen zu erstellen, zu warten und zu injizieren". Klingt für mich wie DI.
Unterzeichnen Sie den
1
@Sign In Ihrem Link heißt es auch: "Das Fakes-Framework kann verwendet werden, um alle .NET-Methoden, einschließlich nicht virtueller und statischer Methoden in versiegelten Typen, zu shimen." Die Tatsache, dass die Isolations-Frameworks in Verbindung mit DI verwendet werden können, bedeutet nicht, dass es sich um DI handelt.
Erik Dietrich
4

Nein, DI ist für Unit-Tests nicht unbedingt erforderlich, hilft aber sehr.

Sie können Fabriken oder Locators verwenden und wie mit DI testen (nur nicht so elegant und erfordern mehr Setup).

Auch Mock Objekte wären wichtig , in Legacy - Systemen , in denen viele Anrufe an Funktionen übertragen werden anstelle von Abhängigkeiten. (Mock Objects können auch ausgiebig in einem ordnungsgemäßen Setup verwendet werden.)

Es kann Setups geben, bei denen das Testen nahezu unmöglich ist. Dies hängt jedoch nicht davon ab, ob eine Abhängigkeitsinjektion verwendet wird oder nicht.

smp7d
quelle
3

Nein , die Abhängigkeitsinjektion ist für Komponententests nicht unbedingt erforderlich.

Die Abhängigkeitsinjektion hilft, wenn Sie eine Klasse haben, die eine abhängige Klasseninstanz benötigt, um eine Teilverarbeitung durchzuführen. Anstelle von DI können Sie die Logik einer Geschäftsmethode in einen Daten-Gethering-Teil (der nicht Unit-Test-fähig ist) und einen Berechnungsteil, der Unit-Test-fähig ist, unterteilen.

Beispiel (mit DI) Diese Implementierung ist abhängig von Employee, Account, ...

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

Nach Trennung von Datenerfassung und Berechnung:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

Der Berechnungsteil kann leicht ohne Abhängigkeitsinjektion getestet werden.

k3b
quelle
1
  • Die Abhängigkeitsinjektion ist für Einheitentests nicht wesentlich

  • Die Umkehrung der Kontrolle ist dagegen unerlässlich, wenn Sie eine Implementierung gegen eine andere austauschen möchten.

CodeART
quelle
0

Ja, es gibt Alternativen zur Verwendung von DI zur Isolierung.

Eine Alternative besteht darin, Fabriken oder einen ServiceLocator zu verwenden, der aus Tests konfiguriert werden kann, um Scheinobjekte anstelle von echten Objekten zurückzugeben.

Eine andere Möglichkeit ist die Verwendung eines geeigneten Isolationsrahmens oder eines Verspottungswerkzeugs. Solche Tools gibt es für nahezu jede moderne Programmiersprache (zumindest für Java, C #, Ruby und Python). Sie können eine getestete Klasse von der Implementierung anderer Klassen / Typen isolieren, selbst wenn die getestete Klasse ihre Abhängigkeiten direkt instanziiert.

Rogério
quelle