Ich bin ziemlich neu in der Welt der Unit-Tests und habe gerade beschlossen, diese Woche eine Testabdeckung für meine vorhandene App hinzuzufügen.
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 völlig 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 neu zu schreiben, was ich bereits in der Methode selbst geschrieben habe.
Meine Tests scheinen nur so eng an die Methode gebunden zu sein (Testen aller Codepfade, wobei erwartet wird, dass einige innere Methoden mit bestimmten Argumenten mehrmals aufgerufen werden), dass es den Anschein hat, als würden die Tests fehlschlagen, wenn ich die Methode jemals umgestalte, selbst wenn die Das endgültige Verhalten der Methode hat sich nicht geändert.
Dies ist nur ein Gefühl, und wie bereits erwähnt, habe ich keine Erfahrung mit Tests. 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.
Bearbeiten: Ich würde mich gerne 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.
quelle
Antworten:
Ich denke du machst es falsch.
Ein Unit-Test sollte:
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 besessen davon: Eine 100% ige Abdeckung ist keine Voraussetzung.
Wenn Ihre Methode öffentliche Methoden in anderen Klassen aufruft und diese Aufrufe von Ihrer Schnittstelle garantiert werden, können Sie mithilfe eines Verspottungsframeworks testen, ob diese Aufrufe ausgeführt werden.
Sie sollten die Methode selbst (oder den von ihr verwendeten internen Code) nicht verwenden, um das erwartete Ergebnis dynamisch zu generieren. Das erwartete Ergebnis sollte in Ihrem Testfall fest codiert sein, damit es sich nicht ändert, wenn sich die Implementierung ändert. Hier ist ein vereinfachtes Beispiel dafür, was ein Komponententest tun sollte:
Beachten Sie, dass die Berechnung des Ergebnisses nicht überprüft wird - nur, dass das Ergebnis korrekt ist. Fügen Sie immer einfachere Testfälle wie die oben genannten hinzu, bis Sie so viele Szenarien wie möglich behandelt haben. Verwenden Sie Ihr Code-Coverage-Tool, um festzustellen, ob Sie interessante Pfade verpasst haben.
quelle
Für Unit-Tests empfand ich sowohl Test Driven (Tests zuerst, Code Second) als auch Code First, Test Second als äußerst nützlich.
Anstatt Code zu schreiben, dann Test schreiben. 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 jeden einen Test. Ich finde das Schreiben von Tests schneller, aber aufwändiger als das Codieren selbst. Die Tests sollten die Absicht testen. Denken Sie auch an die Absichten, mit denen Sie in der Testschreibphase Eckfälle finden. Und natürlich kann es beim Schreiben von Tests vorkommen, dass eine der wenigen Verwendungszwecke einen Fehler verursacht (etwas, das ich häufig finde, und ich bin sehr froh, dass dieser Fehler keine Daten beschädigt und nicht aktiviert hat).
Testen ist jedoch fast wie zweimaliges Codieren. Tatsächlich hatte ich Anwendungen, bei denen es mehr Testcode (Menge) als Anwendungscode gab. Ein Beispiel war eine sehr komplexe Zustandsmaschine. Ich musste sicherstellen, dass das Ganze nach dem Hinzufügen weiterer Logik immer bei 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 diese Maschine, dass ich mir sicher war, dass sie nach Änderungen nicht die Gewinnschwelle erreichen würde, und die Tests haben mir ein paar Mal den Arsch gerettet . Und da Benutzer oder Tester Fehler fanden, bei denen der Flow oder die Eckfälle nicht berücksichtigt wurden, raten Sie mal, fügten Sie Tests hinzu und traten nie wieder auf. 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,
Alle einfachen Beispiele wie
function square(number)
sind großartig und alle und sind wahrscheinlich schlechte Kandidaten, um viel Zeit mit Tests zu verbringen. Diejenigen, die wichtige Geschäftslogik betreiben, hier sind die Tests wichtig. Testen Sie die Anforderungen. Testen Sie nicht nur die Rohrleitungen. Wenn sich die Anforderungen ändern, raten Sie mal, was auch die Tests müssen.Das Testen sollte nicht buchstäblich das dreimalige Aufrufen dieser Funktion sein. Das ist falsch. Überprüfen Sie, ob das Ergebnis und die Nebenwirkungen korrekt sind, nicht die innere Mechanik.
quelle
Es ist erwähnenswert, dass das Nachrüsten von Komponententests in vorhandenen Code weitaus schwieriger ist, als die Erstellung dieses Codes zunächst mit Tests voranzutreiben. Das ist eine der großen Fragen im Umgang mit Legacy-Anwendungen ... wie man Unit-Tests durchführt? Dies wurde schon oft gestellt (so dass Sie möglicherweise als betrogene 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.
quelle
Schreiben Sie keine Tests, um eine vollständige Abdeckung Ihres Codes zu erhalten. Schreiben Sie Tests, die Ihre Anforderungen garantieren. Möglicherweise finden Sie unnötige Codepfade. Umgekehrt, wenn sie notwendig sind, sind sie da, um irgendeine 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 zuerst einen Test. Nur einer. Dann haben Sie den ersten Schritt in der testgetriebenen Entwicklung getan.
quelle
Beim Unit-Test geht es um die Ausgabe, die Sie von einer Funktion / Methode / Anwendung erhalten. Es spielt überhaupt 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 neige dazu, mich hinzusetzen und zu schreiben, was eine Methode bei bestimmten Eingabewerten oder 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.
quelle
Versuchen Sie, einen Komponententest zu schreiben, bevor Sie die zu testende Methode schreiben.
Das wird Sie definitiv dazu zwingen, ein wenig 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.
quelle
Tests sollen die Wartbarkeit verbessern. Wenn Sie eine Methode ändern und ein Test unterbrochen wird, kann dies eine gute Sache sein. Wenn Sie Ihre Methode hingegen als Black Box betrachten, sollte es keine Rolle spielen, was sich in der Methode befindet. Tatsache ist, dass Sie sich für einige Tests lustig machen 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 arbeiten, wie er in Ihrer App ausgeführt wird. Dann können Sie es als Black Box behandeln.
Dies liegt daran, dass Sie Ihre Tests schreiben, nachdem Sie Ihren Code geschrieben haben. Wenn Sie es umgekehrt machen würden (haben Sie die Tests zuerst geschrieben), würde es sich nicht so anfühlen.
quelle