it-swarm.com.de

Neu in Unit-Tests, wie schreibt man großartige Tests?

Ich bin ziemlich neu in der Welt der Unit-Tests und habe mich diese Woche dazu entschlossen, die Testabdeckung für meine bestehende App zu erweitern.

Dies ist eine große Aufgabe, vor allem wegen der Anzahl der zu testenden Klassen, aber auch, weil das Schreiben von Tests für mich alles neu ist.

Ich habe bereits Tests für eine Reihe von Klassen geschrieben, aber jetzt frage ich mich, ob ich es richtig mache.

Wenn ich Tests für eine Methode schreibe, habe ich das Gefühl, ein zweites Mal zu schreiben, was ich bereits in der Methode selbst geschrieben habe.
Meine Tests scheinen nur so eng an die Methode gebunden zu sein (alle Codepfade zu testen, wobei erwartet wird, dass einige innere Methoden mit bestimmten Argumenten mehrmals aufgerufen werden), dass es den Anschein hat, als ob ich die Methode, die Tests, überarbeite schlägt fehl, auch wenn sich das endgültige Verhalten der Methode nicht geändert hat.

Dies ist nur ein Gefühl, und wie bereits erwähnt, habe ich keine Erfahrung mit dem Testen. Wenn einige erfahrene Tester mir Ratschläge geben könnten, wie man großartige Tests für eine vorhandene App schreibt, wäre das sehr dankbar.

Edit: Ich möchte mich bei Stack Overflow bedanken. Ich hatte großartige Eingaben in weniger als 15 Minuten, die mehr von den Stunden des Online-Lesens beantworteten, die ich gerade gemacht habe.

245
pixelastic

Meine Tests scheinen nur so eng an die Methode gebunden zu sein (alle Codepfade zu testen, wobei erwartet wird, dass einige innere Methoden mit bestimmten Argumenten mehrmals aufgerufen werden), dass die Tests selbst dann fehlschlagen, wenn ich die Methode jemals überarbeite Das endgültige Verhalten der Methode hat sich nicht geändert.

Ich denke du machst es falsch.

Ein Komponententest sollte:

  • testen Sie eine Methode
  • geben Sie einige spezifische Argumente für diese Methode an
  • testen Sie, ob das Ergebnis den Erwartungen entspricht

Es sollte nicht in die Methode schauen, um zu sehen, was sie tut, daher sollte das Ändern der Interna nicht dazu führen, dass der Test fehlschlägt. Sie sollten nicht direkt testen, ob private Methoden aufgerufen werden. Wenn Sie herausfinden möchten, ob Ihr privater Code getestet wird, verwenden Sie ein Tool zur Codeabdeckung. Aber seien Sie nicht davon besessen: Eine 100-prozentige Abdeckung ist keine Voraussetzung.

Wenn Ihre Methode öffentliche Methoden in anderen Klassen aufruft und diese Aufrufe von Ihrer Schnittstelle garantiert werden, können Sie testen, ob diese Aufrufe mithilfe eines spöttischen Frameworks ausgeführt werden.

Sie sollten die Methode selbst (oder einen der von ihr verwendeten internen Codes) nicht verwenden, um das erwartete Ergebnis dynamisch zu generieren. Das erwartete Ergebnis sollte in Ihrem Testfall fest programmiert sein, damit es sich nicht ändert, wenn sich die Implementierung ändert. Hier ist ein vereinfachtes Beispiel dafür, was ein Komponententest tun sollte:

testAdd()
{
    int x = 5;
    int y = -2;
    int expectedResult = 3;

    Calculator calculator = new Calculator();
    int actualResult = calculator.Add(x, y);
    Assert.AreEqual(expectedResult, actualResult);
}

Beachten Sie, dass nicht überprüft wird, wie das Ergebnis berechnet wird, sondern nur, ob das Ergebnis korrekt ist. Fügen Sie immer mehr einfache Testfälle wie die oben genannten hinzu, bis Sie so viele Szenarien wie möglich abgedeckt haben. Verwenden Sie Ihr Code-Coverage-Tool, um festzustellen, ob Sie interessante Pfade verpasst haben.

171
Mark Byers

Für Unit-Tests empfand ich sowohl Test Driven (Tests zuerst, Code an zweiter Stelle) als auch Code an erster Stelle und Test an zweiter Stelle als äußerst nützlich.

Anstatt Code zu schreiben, schreiben Sie test. Schreiben Sie Code und schauen Sie sich an, was Sie DENKEN, dass der Code tun sollte. Denken Sie über alle Verwendungszwecke nach und schreiben Sie dann für jede einen Test. Ich finde Schreibtests schneller, aber aufwändiger als das Programmieren. Die Tests sollten die Absicht testen. Denken Sie auch an die Absichten, mit denen Sie in der Phase des Testschreibens auf Eckfälle stoßen. Und natürlich können Sie beim Schreiben von Tests feststellen, dass einer der wenigen Verwendungszwecke einen Fehler verursacht (etwas, das ich oft finde, und ich bin sehr froh, dass dieser Fehler keine Daten verfälscht hat und nicht überprüft wurde).

Testen ist jedoch fast so, als würde man zweimal codieren. In der Tat hatte ich Anwendungen, bei denen es mehr Testcode (Menge) als Anwendungscode gab. Ein Beispiel war eine sehr komplexe Zustandsmaschine. Ich musste sicherstellen, dass, nachdem ich mehr Logik hinzugefügt hatte, das Ganze immer auf allen vorherigen Anwendungsfällen funktionierte. Und da es ziemlich schwierig war, diese Fälle anhand des Codes zu verfolgen, hatte ich eine so gute Testsuite für dieses Gerät, dass ich mir sicher war, dass es nach Änderungen nicht kaputt gehen würde, und die Tests haben mir ein paar Mal den Arsch gerettet . Und da Benutzer oder Tester Fehler mit den Flow- oder Corner-Fällen fanden, die nicht berücksichtigt wurden, raten Sie mal, wurden die Tests erweitert und kamen nie wieder vor. Dies gab den Benutzern wirklich Vertrauen in meine Arbeit und machte das Ganze super stabil. Und wenn es aus Performancegründen neu geschrieben werden musste, raten Sie mal, es hat dank der Tests bei allen Eingaben wie erwartet funktioniert.

Alle einfachen Beispiele wie function square(number) sind großartig und wahrscheinlich schlechte Kandidaten, um viel Zeit mit Testen zu verbringen. Diejenigen, die wichtige Geschäftslogik betreiben, bei denen das Testen wichtig ist. Testen Sie die Anforderungen. Testen Sie nicht nur die Installation. Wenn sich die Anforderungen ändern, raten Sie mal, was die Tests auch müssen.

Das Testen sollte nicht buchstäblich das dreimalige Testen dieser Funktion für die aufgerufene Funktionsleiste sein. Das ist falsch. Überprüfen Sie, ob das Ergebnis und die Nebenwirkungen stimmen, nicht die innere Mechanik.

29
Dmitriy Likhten

Es ist erwähnenswert, dass das Nachrüsten von Komponententests in vorhandenen Code weitaus schwieriger ist , als die Erstellung dieses Codes mit Tests zu steuern. Das ist eine der großen Fragen im Umgang mit Legacy-Anwendungen ... wie man einen Unit-Test durchführt? Dies wurde schon oft gefragt (so dass Sie möglicherweise als vorgetäuschte Frage geschlossen werden), und die Leute landen normalerweise hier:

Verschieben von vorhandenem Code in Test Driven Development

Ich stimme der Buchempfehlung der akzeptierten Antwort zu, aber darüber hinaus sind in den Antworten dort weitere Informationen verlinkt.

18
David

Schreiben Sie keine Tests, um Ihren Code vollständig zu erfassen. Schreiben Sie Tests, die Ihre Anforderungen garantieren. Möglicherweise stellen Sie fest, dass Codepfade nicht erforderlich sind. Umgekehrt, wenn sie notwendig sind, sind sie da, um eine Art von Anforderung zu erfüllen; Finden Sie es, was es ist, und testen Sie die Anforderung (nicht den Pfad).

Halten Sie Ihre Tests klein: ein Test pro Anforderung.

Wenn Sie später eine Änderung vornehmen (oder neuen Code schreiben) müssen, schreiben Sie zunächst einen Test. Nur einer. Dann haben Sie den ersten Schritt in der testgetriebenen Entwicklung getan.

14
Jon Reid

Unit-Tests beziehen sich auf die Ausgabe, die Sie von einer Funktion/Methode/Anwendung erhalten. Es spielt keine Rolle, wie das Ergebnis erzeugt wird, es ist nur wichtig, dass es korrekt ist. Daher ist Ihr Ansatz, Aufrufe an innere Methoden und dergleichen zu zählen, falsch. Ich tendiere dazu, mich hinzusetzen und zu schreiben, was eine Methode bei bestimmten Eingabewerten oder in einer bestimmten Umgebung zurückgeben soll, und dann einen Test zu schreiben, der den tatsächlich zurückgegebenen Wert mit dem vergleicht, was ich mir ausgedacht habe.

13
fresskoma

Versuchen Sie, einen Komponententest zu schreiben, bevor Sie die zu testende Methode schreiben.

Das wird Sie auf jeden Fall dazu zwingen, etwas anders darüber nachzudenken, wie die Dinge gemacht werden. Sie haben keine Ahnung, wie die Methode funktionieren wird, nur was sie tun soll.

Sie sollten immer die Ergebnisse der Methode testen und nicht, wie die Methode diese Ergebnisse erhält.

8
Justin Niessner

tests sollen die Wartbarkeit verbessern. Wenn Sie eine Methode ändern und ein Test abbricht, ist kann eine gute Sache. Wenn Sie Ihre Methode hingegen als Black Box betrachten, sollte es keine Rolle spielen, was sich in der Methode befindet. Tatsache ist, dass Sie Dinge für einige Tests verspotten müssen, und in diesen Fällen können Sie die Methode wirklich nicht als Black Box behandeln. Das einzige, was Sie tun können, ist, einen Integrationstest zu schreiben. Sie laden eine vollständig instanziierte Instanz des zu testenden Dienstes und lassen ihn so tun, als würde er in Ihrer App ausgeführt. Dann können Sie es als Black Box behandeln.

When I'm writing tests for a method, I have the feeling of rewriting a second time what I          
already wrote in the method itself.
My tests just seems so tightly bound to the method (testing all codepath, expecting some    
inner methods to be called a number of times, with certain arguments), that it seems that
if I ever refactor the method, the tests will fail even if the final behavior of the   
method did not change.

Dies liegt daran, dass Sie Ihre Tests schreiben, nachdem Sie Ihren Code geschrieben haben. Wenn Sie es umgekehrt machen würden (schreiben Sie die Tests zuerst), würde es sich nicht so anfühlen.

4
hvgotcodes