it-swarm.dev

Was sollten Sie mit Unit-Tests testen?

Ich bin frisch vom College und fange nächste Woche irgendwo mit der Universität an. Wir haben Unit-Tests gesehen, aber wir haben sie irgendwie nicht oft benutzt. und alle reden über sie, also dachte ich mir, ich sollte vielleicht etwas tun.

Das Problem ist, ich weiß nicht was zu testen. Soll ich den allgemeinen Fall testen? Der Edge-Fall? Woher weiß ich, dass eine Funktion angemessen abgedeckt ist?

Ich habe immer das schreckliche Gefühl, dass ein Test zwar beweist, dass eine Funktion für einen bestimmten Fall funktioniert, es jedoch völlig nutzlos ist, zu beweisen, dass die Funktion funktioniert, Punkt.

128
zneak

Meine persönliche Philosophie war bisher:

  1. Testen Sie den allgemeinen Fall von allem, was Sie können. Hier erfahren Sie, wann dieser Code nach einer Änderung beschädigt wird (was meiner Meinung nach der größte Vorteil des automatisierten Komponententests ist).
  2. Testen Sie die Edge-Fälle einiger ungewöhnlich komplexer Codes, von denen Sie glauben, dass sie wahrscheinlich Fehler enthalten.
  3. Wenn Sie einen Fehler finden, schreiben Sie einen Testfall, um ihn zu behandeln, bevor Sie ihn beheben
  4. Fügen Sie weniger kritischen Code Edge-Case-Tests hinzu, wenn jemand Zeit zum Töten hat.
124
Fishtoaster

Unter der Fülle von Antworten hat bisher niemand Äquivalenzaufteilung und Grenzwertanalyse angesprochen, wichtige Überlegungen bei der Beantwortung der vorliegenden Frage. Alle anderen Antworten sind zwar nützlich, aber qualitativ, aber es ist möglich - und vorzuziehen -, hier quantitativ zu sein. @fishtoaster bietet einige konkrete Richtlinien, die nur unter die Deckung der Testquantifizierung blicken, aber die Äquivalenzpartitionierung und die Grenzwertanalyse ermöglichen es uns, bessere Ergebnisse zu erzielen.

Bei der Äquivalenzpartitionierung teilen Sie die Menge aller möglichen Eingaben basierend auf den erwarteten Ergebnissen in Gruppen ein. Jede Eingabe von einer Gruppe führt zu äquivalenten Ergebnissen, daher werden solche Gruppen als Äquivalenzklassen bezeichnet. (Beachten Sie, dass äquivalente Ergebnisse nicht identische Ergebnisse bedeuten.)

Als einfaches Beispiel betrachten wir ein Programm, das Kleinbuchstaben ASCII Zeichen in Großbuchstaben) umwandeln soll. Andere Zeichen sollten einer Identitätstransformation unterzogen werden, d. H. Unverändert bleiben. Hier ist eine mögliche Aufteilung in Äquivalenzklassen:

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | [email protected]#,/"... | [email protected]#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

In der letzten Spalte wird die Anzahl der Testfälle angegeben, wenn Sie alle auflisten. Technisch gesehen würden Sie nach @ fishtoasters Regel 1 52 Testfälle einschließen - alle für die ersten beiden oben angegebenen Zeilen fallen unter den "allgemeinen Fall". Die Regel 2 von @ fishtoaster würde auch einige oder alle aus den obigen Zeilen 3 und 4 hinzufügen. Beim Testen der Äquivalenzpartitionierung ist jedoch jeder Test in jeder Äquivalenzklasse ausreichend. Wenn Sie "a" oder "g" oder "w" auswählen, testen Sie denselben Codepfad. Somit haben Sie insgesamt 4 Testfälle anstelle von 52+.

Die Grenzwertanalyse empfiehlt eine leichte Verfeinerung: Im Wesentlichen deutet dies darauf hin, dass nicht jedes Mitglied einer Äquivalenzklasse äquivalent ist. Das heißt, Werte an Grenzen sollten auch als eigenständig testwürdig angesehen werden. (Eine einfache Rechtfertigung dafür ist der berüchtigte Fehler nacheinander !) Somit könnten Sie für jede Äquivalenzklasse 3 Testeingaben haben. Wenn ich mir die Eingabedomäne oben ansehe - und mit etwas Wissen über ASCII -Werte) - könnte ich mir diese Testfall-Eingaben einfallen lassen:

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

(Sobald Sie mehr als 3 Grenzwerte erhalten, die darauf hindeuten, dass Sie Ihre ursprünglichen Abgrenzungen der Äquivalenzklassen überdenken möchten, war dies jedoch so einfach, dass ich sie nicht überarbeitet habe.) Die Grenzwertanalyse bringt uns also auf den Punkt 17 Testfälle - mit einem hohen Vertrauen in die vollständige Abdeckung - im Vergleich zu 128 Testfällen für umfassende Tests. (Ganz zu schweigen davon, dass die Kombinatorik vorschreibt, dass umfassende Tests für jede reale Anwendung einfach nicht durchführbar sind!)

68
Michael Sorens

Wahrscheinlich ist meine Meinung nicht zu beliebt. Aber ich schlage vor, dass Sie mit Unit-Tests sparsam umgehen. Wenn Sie zu viele Komponententests haben, verbringen Sie möglicherweise die Hälfte Ihrer Zeit oder mehr mit der Wartung von Tests und nicht mit der eigentlichen Codierung.

Ich schlage vor, dass Sie Tests für Dinge schreiben, bei denen Sie ein schlechtes Gefühl im Bauch haben oder die sehr wichtig und/oder elementar sind. IMHO-Unit-Tests sind kein Ersatz für gute technische und defensive Codierung. Derzeit arbeite ich an einem Projekt, das mehr oder weniger unbrauchbar ist. Es ist wirklich stabil, aber ein Schmerz zu refaktorisieren. Tatsächlich hat in einem Jahr niemand diesen Code berührt und der Software-Stack, auf dem er basiert, ist 4 Jahre alt. Warum? Weil es mit Unit-Tests überfüllt ist, um genau zu sein: Unit-Tests und automatisierte Integrationstests. (Schon mal was von Gurken und dergleichen gehört?) Und hier ist das Beste: Diese (noch) unbrauchbare Software wurde von einem Unternehmen entwickelt, dessen Mitarbeiter Pioniere in der testgetriebenen Entwicklungsszene sind. : D.

Mein Vorschlag lautet also:

  • Beginnen Sie mit dem Schreiben von Tests nach Sie haben das Grundgerüst entwickelt, andernfalls kann Refactoring schmerzhaft sein. Als Entwickler, der für andere entwickelt, werden die Anforderungen nie gleich zu Beginn erfüllt.

  • Stellen Sie sicher, dass Ihre Unit-Tests schnell durchgeführt werden können. Wenn Sie Integrationstests (wie Gurken) haben, ist es in Ordnung, wenn diese etwas länger dauern. Aber lange Tests machen keinen Spaß, glauben Sie mir. (Die Leute vergessen alle Gründe, warum C++ weniger populär geworden ist ...)

  • Überlassen Sie dieses TDD-Zeug den TDD-Experten.

  • Und ja, manchmal konzentrieren Sie sich auf die Edge-Fälle, manchmal auf die häufigsten Fälle, je nachdem, wo Sie das Unerwartete erwarten. Wenn Sie immer das Unerwartete erwarten, sollten Sie Ihren Workflow und Ihre Disziplin wirklich überdenken. ;-);

20
Philip

Wenn Sie zuerst mit Test Driven Development testen, liegt Ihre Abdeckung im Bereich von 90% oder höher, da Sie keine Funktionen hinzufügen, ohne zuvor einen fehlgeschlagenen Komponententest dafür zu schreiben.

Wenn Sie nachträglich Tests hinzufügen, kann ich nicht genug empfehlen, dass Sie eine Kopie von Effektiv mit Legacy-Code arbeiten von Michael Feathers erhalten und sich einige davon ansehen Techniken zum Hinzufügen von Tests zu Ihrem Code und zum Refactoring Ihres Codes, um ihn testbarer zu machen.

8
Paddyslacker

Wenn Sie anfangen, Test Driven Development Praktiken zu befolgen, werden sie Anleitung Sie durch den Prozess sortieren und wissen, was zu testen ist, wird natürlich kommen. Einige Orte, um zu beginnen:

Tests stehen an erster Stelle

Schreiben Sie niemals Code, bevor Sie die Tests schreiben. Eine Erklärung finden Sie unter Rot-Grün-Refaktor-Wiederholung.

Regressionstests schreiben

Wenn Sie auf einen Fehler stoßen, schreiben Sie einen Testfall und stellen Sie sicher, dass dieser fehlschlägt . Wenn Sie einen Fehler nicht durch einen fehlerhaften Testfall reproduzieren können, haben Sie ihn nicht wirklich gefunden.

Rot-Grün-Refaktor-Wiederholung

Rot: Schreiben Sie zunächst einen grundlegenden Test für das Verhalten, das Sie implementieren möchten. Stellen Sie sich diesen Schritt vor, als würden Sie einen Beispielcode schreiben, der die Klasse oder Funktion verwendet, an der Sie arbeiten. Stellen Sie sicher, dass es kompiliert/keine Syntaxfehler aufweist und dass es fehlschlägt . Dies sollte offensichtlich sein: Sie haben keinen Code geschrieben, also muss er fehlschlagen, oder? Das Wichtigste, was Sie hier lernen müssen, ist, dass Sie niemals sicher sein können, dass der Test, wenn er mindestens einmal fehlschlägt, aufgrund von etwas, das Sie aus einem falschen Grund getan haben, nicht erfolgreich ist.

Grün: Schreiben Sie den einfachsten und dümmsten Code, der den Test tatsächlich besteht. Versuche nicht schlau zu sein. Selbst wenn Sie sehen, dass es einen offensichtlichen Edge-Fall gibt, der Test jedoch berücksichtigt, nicht Code schreiben, um damit umzugehen (aber vergessen Sie nicht den Edge-Fall: Sie werden ihn brauchen später). Die Idee ist, dass jeder Code, den Sie schreiben, jeder if, jeder try: ... except: ... sollte durch einen Testfall begründet werden. Der Code muss nicht elegant, schnell oder optimiert sein. Sie möchten nur, dass der Test bestanden wird.

Refactor: Bereinigen Sie Ihren Code und machen Sie die Methodennamen richtig. Überprüfen Sie, ob der Test noch besteht. Optimieren. Führen Sie den Test erneut aus.

Wiederholen: Sie erinnern sich an den Edge-Fall, den der Test nicht behandelt hat, oder? Jetzt ist es also ein großer Moment. Schreiben Sie einen Testfall, der diese Situation abdeckt, beobachten Sie, wie er fehlschlägt, schreiben Sie Code, sehen Sie, wie er erfolgreich ist, und überarbeiten Sie ihn.

Test your code

Sie arbeiten an einem bestimmten Code, und genau das möchten Sie testen. Dies bedeutet, dass Sie keine Bibliotheksfunktionen, die Standardbibliothek oder Ihren Compiler testen sollten. Vermeiden Sie es auch, die "Welt" zu testen. Dies beinhaltet: Aufrufen externer Web-APIs, einige datenbankintensive Dinge usw. Wann immer Sie versuchen können, es zu verspotten (erstellen Sie ein Objekt, das derselben Schnittstelle folgt, aber statische, vordefinierte Daten zurückgibt).

6
Ryszard Szopa

Beginnen Sie bei Komponententests mit dem Testen, ob es das tut, wofür es entwickelt wurde. Das sollte der allererste Fall sein, den Sie schreiben. Wenn ein Teil des Designs "es sollte eine Ausnahme auslösen, wenn Sie Junk übergeben" ist, testen Sie dies ebenfalls, da dies Teil des Designs ist.

Beginnen Sie damit. Wenn Sie Erfahrung mit den grundlegendsten Tests haben, werden Sie feststellen, ob dies ausreicht oder nicht, und andere Aspekte Ihres Codes erkennen, die getestet werden müssen.

3
Bryan Oakley

Die Aktienantwort lautet "teste alles, was möglicherweise kaputt gehen könnte" .

Was ist zu einfach zu brechen? Datenfelder, hirntote Eigenschaftszugriffsgeräte und ähnliche Overheads für Boilerplates. Alles andere implementiert wahrscheinlich einen identifizierbaren Teil einer Anforderung und kann von einem Test profitieren.

Natürlich können Ihre Laufleistung - und die Praktiken Ihrer Arbeitsumgebung - variieren.

0
Jeffrey Hantin