Welche Art von Funktionen und / oder Klassen sind unmöglich zu testen und warum

21

Die Hauptausrede des Entwicklers für das Nichtvorhandensein eines guten Komponententests ist "Code ist nicht einheitentestbar". Ich versuche zu verstehen, welche Art von Design und Code nicht Unit-getestet werden kann.

manizzzz
quelle
2
Deine Entschuldigung? Mitarbeiter? Manager? Mit welcher Sprache / welchen Frameworks arbeiten Sie?
1
Viel Legacy-Code in der Anwendung und keine Zeit für ein Redesign.
Knut
4
@gnat: Ich bin anderer Meinung. Die Frage, die Sie zitiert haben, bezieht sich auf Situationen, in denen Komponententests nicht nützlich sind. Die aktuelle Frage betrifft Situationen, die Komponententests erschweren.
Arseni Mourzenko
@ MainMa anscheinend lesen wir verschiedene Fragen. "Ich versuche zu verstehen, welche Art von Design und Code nicht Unit-getestet werden kann." => "Wann ist es angebracht, keinen Komponententest durchzuführen?"
Mücke
1
@manizzzz: Vielleicht möchtest du das in der Frage bearbeiten.
Jmoreno

Antworten:

27

Verschiedene Faktoren können es schwierig machen, den Code als Einheit zu testen. In diesem Fall hilft das Refactoring bei der Verbesserung des Codes, damit dieser testbar ist.

Einige Beispiele für Code, der wahrscheinlich schwer zu testen wäre:

  • Eine 1000-LOC-Funktion,
  • Code, der stark vom globalen Staat abhängt,
  • Code, der konkrete, komplizierte Objekte wie den Datenbankkontext erfordert, anstatt sich auf Schnittstellen und Dependency Injection zu verlassen.
  • Code, der langsam arbeitet ,
  • Spaghetti-Code,
  • Legacy-Code, der jahrelang ohne Rücksicht auf Lesbarkeit oder Wartbarkeit geändert wurde,
  • Schwer zu verstehender Code, der keine Kommentare oder Hinweise auf die ursprüngliche Absicht des Autors enthält (zum Beispiel Code, der Variablennamen wie function pGetDp_U(int i, int i2, string sText).

Beachten Sie, dass das Fehlen einer klaren Architektur das Testen von Code nicht erschwert, da Unit-Tests kleine Teile des Codes betreffen. Eine unklare Architektur würde sich nach wie vor negativ auf die Integration und die Systemtests auswirken.

Arseni Mourzenko
quelle
8
Außerdem ist es schwierig, Code zu testen, der keine Abhängigkeiten von nicht reinen Funktionen wie Zufallszahlen, aktueller Zeit, fest verdrahteten E / A usw. erzeugt.
9000
Es ist trivial, Code so zu testen - Sie brauchen nur das richtige Testwerkzeug, um Ihren Code nicht zu entstellen. Probieren Sie Microsoft Fakes als Beispiel.
gbjbaanb
@ MainMa, ich mag diese Antwort. Würden Sie auch gerne etwas dazu sagen, welche Faktoren verschiedene Tests zu Integrations- und Systemtests führen? Ich weiß, dass ich ähnliche Fragen gestellt habe wie hier in der Vergangenheit, weil ich keine Roadmap hatte, in der erklärt wurde, welche Arten von Tests am besten wo (oder vielleicht am kostengünstigsten wo) abgelegt werden - dachte ich Unit-Tests waren das Einzige.
J Trana
14

Es gibt eine Menge Dinge, die den Unit-Test von Code erschweren. Zufälligerweise erschweren viele davon die Pflege von Code:

  • Gesetz der Demeter- Verstöße.
  • Erstellen von Objekten innerhalb einer Methode, anstatt Abhängigkeiten einzufügen .
  • Enge Kupplung.
  • Schlechter Zusammenhalt.
  • Verlässt sich stark auf Nebenwirkungen.
  • Verlässt sich stark auf Globals oder Singletons.
  • Belichtet nicht viele Zwischenergebnisse. (Ich musste einmal eine zehn Seiten lange mathematische Funktion mit einer einzigen Ausgabe und keinen verfügbaren Zwischenergebnissen testen. Meine Vorgänger haben im Grunde genommen jede Antwort, die der Code gab, hartcodiert.)
  • Hängt stark und direkt von Diensten ab, die schwer zu verspotten sind, wie Datenbanken.
  • Die Laufzeitumgebung unterscheidet sich erheblich von der Entwicklungsumgebung, z. B. von einem eingebetteten Ziel.
  • Einheiten nur in kompilierter Form verfügbar (wie eine Drittanbieter-DLL).
Karl Bielefeldt
quelle
Ich denke, das ist eine hervorragende Antwort. Sie berühren viele Probleme auf Codeebene und globale Statusprobleme. @MainMa hat einige andere Probleme, von denen ich denke, dass sie gültig, aber weniger genau definiert sind. Jeffery Thomas erwähnt I / O und UI. Ich denke, wenn Sie die guten Teile dieser drei Antworten addieren, würden Sie eine große zusammenhängende Antwort haben. Diese Antwort gefällt mir jedoch am besten, weil ich mich auf Code-Antimuster konzentriere.
M2TM
1
Argh - nichts Schlimmeres als Unit-Test-Asserts, die keine Ähnlichkeit mit den Geschäftsanforderungen haben und nur die Ausgabe zu einem bestimmten Zeitpunkt sind - Mocks, die beispielsweise so eingerichtet sind, dass sie dreimal aufgerufen werden? Warum 3? Weil es 3 war, als der Test zum ersten Mal durchgeführt wurde :)
Michael
Enge Kopplung ist nur dann schlecht, wenn sie unangemessen ist. Enge Kopplung im Code, die einen hohen Zusammenhalt aufweist, ist eine Notwendigkeit. Zum Beispiel eine Variablendeklaration, gefolgt von ihrer Verwendung. Eng verbunden, stark kohäsiv.
Dietbuddha
1
Komponententests, bei denen die Ausgabe ohne Geschäftsvorfall / Begründung mit dem Code verglichen wird, werden als Charakterisierungstests bezeichnet. Sie werden bei Wartungsarbeiten verwendet, bei denen es zuvor keine Tests und häufig keine dokumentierten Anforderungen gab, und Sie müssen etwas einfügen, das bei einer Änderung der Ausgabe dieser Funktion nicht mehr funktioniert. Sie sind besser als nichts.
Andy Krouwel
5

Häufige Beispiele für Codeleute, die keinen Komponententest durchführen möchten:

  • Code, der direkt mit I / O interagiert (Lesen von Dateien, direkte Netzwerkaufrufe, ...).
  • Code, der die Benutzeroberfläche direkt aktualisiert.
  • Code, der direkt auf Singletons oder globale Objekte verweist.
  • Code, der den Objekt- oder Unterobjektstatus implizit ändert.

Mit einem Mock-Framework können alle diese Beispiele einem Komponententest unterzogen werden. Es ist nur Arbeit, die Scheinersetzungen für die internen Abhängigkeiten einzurichten.

Dinge, die wirklich nicht einheitlich getestet werden können:

  • Endlosschleifen (für einen Thread-Manager, einen Treiber oder eine andere Art von lang laufendem Code)
  • Bestimmte Arten von direkten Assemblierungsvorgängen (die von einigen Sprachen unterstützt werden)
  • Code, der privilegierten Zugriff erfordert (nicht unmöglich, nur keine gute Idee)
Jeffery Thomas
quelle
2

Dies sind einige Bereiche, die das Schreiben von Komponententests erschweren können. Ich möchte jedoch betonen, dass dies nicht bedeutet, dass Sie nützliche Techniken sofort außer Acht lassen sollten, da sie Ihre Tests möglicherweise etwas komplexer machen. Wie bei jeder Codierung sollten Sie Ihre eigene Analyse durchführen, um festzustellen, ob der Nutzen die Kosten übersteigt, und nicht blindlings akzeptieren, was einige zufällige Typen im Netz posten.

Schlecht geschrieben von entworfenem Code

  • ungeeignete Kupplung (normalerweise dichte Kupplung, wo es nicht sein sollte)
  • Code für Küchenspülen (wo eine Funktion viel zu viel Logik / Verantwortlichkeiten hat)

Staatszugehörigkeit in einem anderen Umfang

Die Kosten für die meisten von ihnen sind außer Kontrolle geraten, es sei denn, Sie wissen, was Sie tun. Leider wissen viele nicht, wie man diese Techniken einsetzt, um beispielsweise die Komplexität zu testen.

  • Singletons
  • Globals
  • Verschlüsse

Externer / Systemstatus

  • Hardware- / Geräteabhängigkeiten
  • Netzwerkabhängigkeiten
  • Dateisystemabhängigkeiten
  • Abhängigkeiten zwischen Prozessen
  • Andere Systemaufrufabhängigkeiten

Parallelität

  • Einfädeln (Sperren, kritische Abschnitte usw.)
  • gabeln
  • Coroutinen
  • Rückrufe
  • Signalhandler (nicht alle, aber einige)
dietbuddha
quelle
2

Es gibt keinen Code, der nicht getestet werden kann. Es gibt jedoch ein paar Beispiele für Code, der WIRKLICH, WIRKLICH schwer zu testen ist (bis zu dem Punkt, dass sich der Aufwand möglicherweise nicht mehr lohnt):

Hardware-Interaktionen - Wenn der Code die Hardware direkt manipuliert (z. B. in ein Register schreiben, um ein physisches Gerät zu verschieben), ist das Testen der Einheit möglicherweise zu schwierig oder zu teuer. Wenn Sie für den Test echte Hardware verwenden, kann es teuer werden, entsprechendes Feedback in das Testkabel zu bekommen (noch mehr Geräte!), Und wenn Sie dies nicht tun, müssen Sie das genaue Verhalten von physischen Objekten emulieren - kein kleiner Trick einige Fälle.

Interaktionen mit der Uhr - Dies ist normalerweise einfacher, da es fast immer möglich ist, die Funktionen der Systemuhr trivial auszutricksen. Wenn dies jedoch nicht möglich ist, können diese Tests nicht mehr verwaltet werden. Auf Echtzeit basierende Tests dauern in der Regel sehr lange und sind meiner Erfahrung nach sehr spröde, da die Systemlasten zu einer längeren Dauer führen, als sie sollten , was zu Phantomtestfehlern führt.

Michael Kohne
quelle
0

Meine drei Hauptgruppen dafür sind:

  • Code, der auf externen Diensten beruht

  • Systeme, die es Testern nicht ermöglichen, den Status unabhängig von der Anwendung zu ändern.

  • Testumgebungen, die das Produktionssetup nicht replizieren.

Das habe ich am meisten als Entwickler erlebt, der zum QS-Ingenieur wurde.

Michael Durrant
quelle