Ist die Testabdeckung ein angemessenes Maß für die Codequalität?

20

Wenn ich Code habe, der eine Testabdeckung von 80% aufweist (alle Tests bestehen), kann ich dann sagen, dass er von höherer Qualität ist als Code ohne Testabdeckung?

Oder ist es fair zu sagen, dass es besser zu warten ist?

David_001
quelle
2
100% Deckung bedeutet nicht, dass es gut getestet wurde. 0% bedeutet jedoch, dass es überhaupt nicht getestet wurde.
Mouviciel
1
Technisch nein. Praktisch ja. Die Erfahrung hat vielen Software-Ingenieuren und -Testern beigebracht, dass bei einer Codeabdeckung von etwa 80% die Arten von Fehlern, für die ein Komponententest angemessen ist, allmählich abflachen. Es ist das Pareto-Prinzip. Grundsätzlich haben Sie, sobald Sie an dem Punkt angelangt sind, an dem Sie 80% des Codes abdecken, unabhängig von der Qualität Ihrer Tests, wahrscheinlich die 20% des Codes getestet, die die meisten potenziellen Probleme ziemlich gründlich verursachen. Dies ist keine absolute, sondern eine konventionelle Weisheit. Sie müssen gründlicher sein, wenn das Leben von Ihren Tests abhängt.
Calphool
@ JoeRounceville Ich bin nicht sicher ... Ich kann eine hohe Testabdeckung erzielen, während ich nichts wirklich Nützliches teste. Coverage gibt lediglich an, wie viel Code von der Testsuite berührt wird, nicht, ob die Tests aussagekräftig sind.
Andres F.
1
@AndresF. Deshalb habe ich "technisch nein, praktisch ja" gesagt. Menschen sind (im Allgemeinen) keine Idioten. Sie testen (im Allgemeinen) nicht nur einfache Fälle. So, basierend auf Erfahrung , viele Geschäfte stoppen irgendwo rund 80% Deckung, so dass die (ziemlich sicher) Annahme , dass ihre Leute nicht morons.
Calphool

Antworten:

24

Im engeren Sinne ist es nicht fair, Ansprüche geltend zu machen, bis die Qualität der Testsuite feststeht. Das Bestehen von 100% der Tests ist nicht sinnvoll, wenn die meisten Tests trivial sind oder sich wiederholen.

Die Frage ist: Hat einer dieser Tests in der Projektgeschichte Fehler aufgedeckt? Das Ziel eines Tests ist es, Fehler zu finden. Und wenn nicht, scheiterten sie als Tests. Anstatt die Codequalität zu verbessern, geben sie Ihnen möglicherweise nur ein falsches Sicherheitsgefühl.

Um Ihre Testdesigns zu verbessern, können Sie (1) Whitebox-Techniken, (2) Blackbox-Techniken und (3) Mutationstests verwenden.

(1) Hier einige gute Whitebox-Techniken, die Sie für Ihre Testdesigns anwenden können. Ein Whitebox-Test wird unter Berücksichtigung des spezifischen Quellcodes erstellt. Ein wichtiger Aspekt beim Testen von Whiteboxen ist die Codeabdeckung:

  • Wird jede Funktion aufgerufen? [Funktionsumfang]
  • Wird jede Anweisung ausgeführt? [Berichterstattung - Sowohl die funktionale Berichterstattung als auch die Berichterstattung sind sehr einfach, aber besser als nichts]
  • Haben Sie für jede Entscheidung (wie ifoder while) einen Test, der die Richtigkeit erzwingt, und einen anderen, der die Falschheit erzwingt? [Entscheidungsberichterstattung]
  • Hat jeder Unterausdruck für jede Bedingung, die eine Konjunktion (Verwendungen &&) oder eine Disjunktion (Verwendungen ||) ist, einen Test, bei dem er wahr / falsch ist? [Bedingungsüberdeckung]
  • Loop-Coverage: Haben Sie einen Test, der 0 Iterationen, 1 Iteration, 2 Iterationen erzwingt?
  • Wird jeder breakvon einer Schleife abgedeckt?

(2) Blackbox-Techniken werden verwendet, wenn die Anforderungen verfügbar sind, der Code selbst jedoch nicht. Diese können zu hochwertigen Tests führen:

  • Decken Ihre Blackbox-Tests mehrere Testziele ab? Sie möchten, dass Ihre Tests "fett" sind: Sie testen nicht nur Feature X, sondern auch Y und Z. Das Zusammenspiel verschiedener Features ist eine großartige Möglichkeit, um Fehler zu finden.
  • Der einzige Fall, in dem Sie keine "Fett" -Tests wünschen, ist das Testen eines Fehlerzustands. Beispiel: Testen auf ungültige Benutzereingaben. Wenn Sie versucht haben, mehrere ungültige Eingabetestziele zu erreichen (z. B. eine ungültige Postleitzahl und eine ungültige Straße), ist es wahrscheinlich, dass ein Fall den anderen maskiert.
  • Berücksichtigen Sie die Eingabetypen und bilden Sie eine "Äquivalenzklasse" für die Eingabetypen. Wenn Ihr Code beispielsweise prüft, ob ein Dreieck gleichseitig ist, werden bei dem Test, bei dem ein Dreieck mit Seiten (1, 1, 1) verwendet wird, wahrscheinlich die gleichen Arten von Fehlern festgestellt wie bei den Testdaten (2, 2, 2) und (3, 3, 3) finden. Es ist besser, Ihre Zeit mit dem Nachdenken über andere Input-Klassen zu verbringen. Wenn Ihr Programm beispielsweise Steuern verarbeitet, möchten Sie einen Test für jede Steuerklasse. [Dies wird als Äquivalenzpartitionierung bezeichnet.]
  • Sonderfälle sind häufig mit Mängeln verbunden. Ihre Testdaten sollten auch Grenzwerte enthalten, z. B. an, über oder unter den Rändern einer Äquivalenzaufgabe. Wenn Sie beispielsweise einen Sortieralgorithmus testen, möchten Sie ein leeres Array, ein Einzelelement-Array, ein Array mit zwei Elementen und dann ein sehr großes Array testen. Sie sollten Grenzfälle nicht nur für die Eingabe berücksichtigen, sondern auch für die Ausgabe. [Dies ist die Call Boundary-Value-Analyse.]
  • Eine andere Technik ist "Fehlervermutung". Haben Sie das Gefühl, wenn Sie eine spezielle Kombination ausprobieren, dass Sie Ihr Programm zum Brechen bringen können? Dann probieren Sie es einfach aus! Denken Sie daran: Ihr Ziel ist es, Fehler zu finden und nicht zu bestätigen, dass das Programm gültig ist . Einige Leute haben das Talent, Fehler zu erraten.

(3) Nehmen wir zum Schluss an, Sie haben bereits viele gute Tests für die Abdeckung von Whiteboxen und angewandte Blackbox-Techniken. Was kannst du noch tun? Es ist Zeit, Ihre Tests zu testen . Eine Technik, die Sie verwenden können, ist das Testen von Mutationen.

Unter Mutationstests nehmen Sie eine Modifikation (Kopie) Ihres Programms vor, in der Hoffnung, einen Fehler zu verursachen. Eine Mutation könnte sein:

Ändern Sie eine Referenz einer Variablen in eine andere Variable. Füge die abs () Funktion ein; Ändern Sie kleiner als zu größer als; Eine Aussage löschen; Ersetzen Sie eine Variable durch eine Konstante. Löschen Sie eine überschreibende Methode. Löschen Sie einen Verweis auf eine Super-Methode. Ändern Sie die Argumentreihenfolge

Erstellen Sie mehrere Dutzend Mutanten an verschiedenen Stellen in Ihrem Programm [das Programm muss zum Testen noch kompiliert werden]. Wenn Ihre Tests diese Fehler nicht finden, müssen Sie jetzt einen Test schreiben, der den Fehler in der mutierten Version Ihres Programms findet. Sobald ein Test den Fehler gefunden hat, haben Sie den Mutanten getötet und können einen anderen versuchen.


Nachtrag : Ich habe vergessen, diesen Effekt zu erwähnen: Bugs neigen zur Häufung . Das heißt, je mehr Fehler Sie in einem Modul finden, desto höher ist die Wahrscheinlichkeit, dass Sie mehr Fehler finden. Wenn Sie also einen Test haben, der fehlschlägt (dh der Test ist erfolgreich, da das Ziel darin besteht, Fehler zu finden), sollten Sie nicht nur den Fehler beheben, sondern auch weitere Tests für das Modul schreiben Techniken oben.

Solange Sie ständig Fehler finden, müssen die Testbemühungen fortgesetzt werden. Nur wenn die Anzahl der gefundenen neuen Fehler sinkt, können Sie sicher sein, dass Sie gute Testbemühungen für diese Entwicklungsphase unternommen haben.

Macneil
quelle
7

Nach einer Definition ist es wartungsfreundlicher, da jede Änderung durch die Tests mit größerer Wahrscheinlichkeit aufgefangen wird.

Die Tatsache, dass Code die Komponententests besteht, bedeutet jedoch nicht, dass er von höherer Qualität ist. Möglicherweise ist der Code mit irrelevanten Kommentaren und unangemessenen Datenstrukturen noch immer schlecht formatiert, kann die Tests jedoch bestehen.

Ich weiß, welchen Code ich lieber pflegen und erweitern möchte.

ChrisF
quelle
7

Code mit absolut keinen Tests kann extrem hohe Qualität, Lesbarkeit, Schönheit und Effizienz (oder völliger Müll) aufweisen. Nein, es ist nicht fair zu sagen, dass Code mit 80% Testabdeckung von höherer Qualität ist als Code ohne Testabdeckung.

Es könnte fair zu sagen , dass Code 80% mit abgedeckt guten Tests ist wahrscheinlich von akzeptabler Qualität, und wahrscheinlich relativ wartbar. Aber es garantiert wirklich wenig.

Joonas Pulakka
quelle
3

Ich würde es mehr refactorable nennen. Refactoring wird extrem einfach, wenn Code mit vielen Tests abgedeckt wird.

Es wäre fair, es als wartbarer zu bezeichnen.

Josip Medved
quelle
2

Ich würde dem wartbaren Teil zustimmen. Michael Feathers hat kürzlich ein Video mit dem Titel " Die tiefe Synergie zwischen Testbarkeit und gutem Design " gepostet, in dem er dieses Thema diskutiert. In dem Vortrag sagt er, dass die Beziehung eine Möglichkeit ist, das heißt, dass gut gestalteter Code testbar ist, aber testbarer Code nicht unbedingt gut gestaltet ist.

Es ist erwähnenswert, dass das Video-Streaming im Video nicht besonders gut ist. Daher kann es sich lohnen, es herunterzuladen, wenn Sie es vollständig ansehen möchten.

Paddyslacker
quelle
-2

Diese Frage stelle ich mir seit einiger Zeit im Zusammenhang mit der "Bedingungsdeckung". Wie wäre es mit dieser Seite von atollic.com "Warum Codeabdeckungsanalyse?"

Technisch gesehen findet die Codeabdeckungsanalyse Bereiche in Ihrem Programm, die nicht von Ihren Testfällen abgedeckt werden, sodass Sie zusätzliche Tests erstellen können, die ansonsten nicht getestete Teile Ihres Programms abdecken. Es ist daher wichtig zu verstehen, dass die Codeabdeckung Ihnen hilft, die Qualität Ihrer Testverfahren und nicht die Qualität des Codes selbst zu verstehen .

Dies scheint hier sehr relevant zu sein. Wenn Sie einen Testfallsatz haben, der es schafft, ein bestimmtes Maß an (Code- oder sonstigem) Abdeckung zu erreichen, rufen Sie den zu testenden Code höchstwahrscheinlich mit einem ziemlich umfassenden Satz von Eingabewerten auf! Dies sagt nicht viel über den zu testenden Code aus (es sei denn, der Code bläst auf oder erzeugt erkennbare Fehler), gibt Ihnen jedoch Vertrauen in Ihren Testfall .

In einem interessanten Necker Cube- Wechsel wird der Testcode nun vom Testcode getestet!

David Tonhofer
quelle
-3

Es gibt viele Möglichkeiten, um sicherzustellen, dass ein Programm das tut, was Sie beabsichtigen, und um sicherzustellen, dass Änderungen keine unbeabsichtigten Auswirkungen haben.

Testen ist eins. Die Vermeidung von Datenmutationen ist eine andere. So ist ein Typensystem. Oder formelle Verifizierung.

Obwohl ich der Meinung bin, dass das Testen im Allgemeinen eine gute Sache ist, bedeutet ein bestimmter Prozentsatz des Testens möglicherweise nicht viel. Ich würde mich lieber auf etwas verlassen, das ohne Tests in Haskell geschrieben wurde, als auf eine gut getestete PHP-Bibliothek

Andrea
quelle
Ist das nur deine Meinung oder kannst du es irgendwie bestätigen?
Mücke
2
Testen ist keine Garantie dafür, dass ein Programm das tut, was Sie beabsichtigen.
Andres F.
1
Dann muss ich mich fragen, was Testen ist
Andrea
@gnat das ist natürlich meine meinung. Trotzdem sagt es, was gesagt wird. Ich habe Haskell als Beispiel für eine Sprache genommen, deren Compiler sehr streng ist und die viele Garantien für die Form der Eingabe, die Typen, die Nebenwirkungen und die Mutation von Daten gibt. Ich habe PHP als Beispiel für eine Sprache genommen, deren Interpreter sehr nachsichtig ist und die nicht einmal eine Spezifikation hat. Selbst wenn keine Tests durchgeführt wurden, führt das Vorhandensein aller Garantien des Systems für Typen und Effekte in der Regel zu einem angemessenen Maß an Zuverlässigkeit. Um dies mit Tests zu kompensieren, müsste man eine sehr umfassende Suite haben
Andrea,
Ich war vielleicht etwas gehetzt, als ich schrieb - ich war am Telefon - aber ich denke immer noch, dass es einen Punkt gibt. Ich möchte mich nicht auf PHP einlassen, aber ich denke, dass es eine objektive Aussage ist, zu sagen, dass Haskell im Vergleich ein viel höheres Maß an Zuverlässigkeit bietet
Andrea,