it-swarm-eu.dev

Sind Asserts oder Unit Tests wichtiger?

Sowohl Asserts als auch Unit-Tests dienen als Dokumentation für eine Codebasis und als Mittel zum Erkennen von Fehlern. Die Hauptunterschiede bestehen darin, dass Asserts als Sanity Checks fungieren und reale Eingaben sehen, während Unit-Tests mit bestimmten simulierten Eingaben ausgeführt werden und Tests gegen eine einzelne genau definierte "richtige Antwort" sind. Was sind die relativen Vorteile der Verwendung von Asserts gegenüber Unit-Tests als Hauptmittel zur Überprüfung der Richtigkeit? Was sollte Ihrer Meinung nach stärker betont werden?

52
dsimcha

Asserts sind nützlich, um Sie über den internen Status des Programms zu informieren. Zum Beispiel, dass Ihre Datenstrukturen einen gültigen Status haben, z. B. dass eine Time -Datenstruktur nicht den Wert von 25:61:61 Enthält. Die von Asserts überprüften Bedingungen sind:

  • Voraussetzungen, die sicherstellen, dass der Anrufer seinen Vertrag einhält,

  • Nachbedingungen, die sicherstellen, dass der Angerufene seinen Vertrag behält, und

  • Invarianten, die sicherstellen, dass die Datenstruktur nach der Rückkehr der Funktion immer eine Eigenschaft enthält. Eine Invariante ist eine Bedingung, die eine Vorbedingung und eine Nachbedingung ist.

Unit-Tests sind nützlich, um Sie über das externe Verhalten des Moduls zu informieren. Ihr Stack hat möglicherweise einen konsistenten Status, nachdem die Methode Push() aufgerufen wurde. Wenn sich die Größe des Stapels jedoch nach dem dreimaligen Aufruf nicht um drei erhöht, ist dies ein Fehler . (Zum Beispiel der triviale Fall, in dem die falsche Implementierung von Push() nur die Asserts und Exits überprüft.)

Genau genommen besteht der Hauptunterschied zwischen Asserts und Unit-Tests darin, dass Unit-Tests Testdaten haben (Werte, mit denen das Programm ausgeführt werden soll), Asserts jedoch nicht. Das heißt, Sie können Ihre Komponententests automatisch ausführen, während Sie dies für Aussagen nicht sagen können. Für diese Diskussion habe ich angenommen, dass Sie über die Ausführung des Programms im Rahmen von Funktionstests höherer Ordnung sprechen (die das gesamte Programm ausführen und keine Module wie Komponententests steuern). Wenn Sie nicht über automatisierte Funktionstests als Mittel zum "Sehen realer Eingaben" sprechen, liegt der Wert eindeutig in der Automatisierung, und somit würden die Komponententests gewinnen. Wenn Sie im Rahmen von (automatisierten) Funktionstests darüber sprechen, siehe unten.

Das, was getestet wird, kann sich überschneiden. Beispielsweise kann die Nachbedingung eines Stack tatsächlich behaupten, dass die Stapelgröße um eins zunimmt. Es gibt jedoch Grenzen für die Ausführung dieser Behauptung: Sollte auch überprüft werden, ob das oberste Element das ist, was gerade hinzugefügt wurde?

Für beide ist das Ziel, die Qualität zu steigern. Für Unit-Tests besteht das Ziel darin, Fehler zu finden. Bei Zusicherungen besteht das Ziel darin, das Debuggen zu vereinfachen, indem ungültige Programmzustände sofort nach ihrem Auftreten beobachtet werden.

Beachten Sie, dass keine Technik die Richtigkeit überprüft. Wenn Sie Unit-Tests mit dem Ziel durchführen, die Richtigkeit des Programms zu überprüfen, werden Sie wahrscheinlich einen uninteressanten Test finden, von dem Sie wissen, dass er funktioniert. Es ist eine psychologische Wirkung: Sie werden alles tun, um Ihr Ziel zu erreichen. Wenn Ihr Ziel darin besteht, Fehler zu finden, werden Ihre Aktivitäten dies widerspiegeln.

Beide sind wichtig und haben ihre eigenen Zwecke.

[Als letzte Anmerkung zu Behauptungen: Um den größtmöglichen Nutzen zu erzielen, müssen Sie sie an allen kritischen Punkten Ihres Programms verwenden und nicht an einigen Schlüsselfunktionen. Andernfalls wäre die ursprüngliche Ursache des Problems möglicherweise maskiert und ohne stundenlanges Debuggen schwer zu erkennen.]

43
Macneil

Wenn Sie über Behauptungen sprechen, denken Sie daran, dass diese auf Knopfdruck ausgeschaltet werden können.

Beispiel für eine sehr schlechte Behauptung:

char *c = malloc(1024);
assert(c != NULL);

Warum ist das so schlimm? Weil keine Fehlerprüfung durchgeführt wird, wenn diese Zusicherung übersprungen wird, weil so etwas wie NDEBUG definiert wurde.

Ein Unit-Test würde (wahrscheinlich) nur den obigen Code fehlerhaft behandeln. Sicher, es hat seinen Job gemacht, indem es Ihnen gesagt hat, dass etwas schief gelaufen ist, oder? Wie wahrscheinlich ist es, dass malloc() unter dem Test fehlschlägt?

Zusicherungen dienen Debugging-Zwecken, wenn ein Programmierer implizieren muss, dass kein "normales" Ereignis dazu führen würde, dass die Zusicherung ausgelöst wird. malloc() Fehler ist in der Tat ein normales Ereignis und sollte daher niemals behauptet werden.

Es gibt viele andere Fälle, in denen Behauptungen verwendet werden, anstatt mit Dingen, die möglicherweise schief gehen, angemessen umzugehen. Dies ist der Grund, warum Behauptungen einen schlechten Ruf erhalten und warum Sprachen wie Go sie nicht enthalten.

Unit-Tests sollen Ihnen sagen, wann etwas, das Sie geändert haben, etwas anderes kaputt gemacht hat. Sie sollen Sie (in den meisten Fällen) davon abhalten, jedes Mal, wenn Sie einen Build erstellen, alle Funktionen des Programms zu durchlaufen (Tester sind wichtig für Releases).

Es gibt wirklich keine eindeutige Korrelation zwischen den beiden, außer dass beide Ihnen sagen, dass etwas schief gelaufen ist. Stellen Sie sich eine Behauptung als Haltepunkt in etwas vor, an dem Sie arbeiten, ohne einen Debugger verwenden zu müssen. Stellen Sie sich einen Komponententest als etwas vor, das Ihnen sagt, ob Sie etwas kaputt gemacht haben, an dem Sie nicht arbeiten.

7
Tim Post

Beide Tools helfen dabei, die Gesamtqualität des von Ihnen erstellten Systems zu verbessern. Viel hängt von der Sprache ab, die Sie verwenden, von der Art der Anwendung, die Sie erstellen, und davon, wo Sie Ihre Zeit am besten verbringen. Ganz zu schweigen davon, dass Sie ein paar Denkschulen haben.

Wenn Sie eine Sprache ohne das Schlüsselwort assert verwenden, können Sie zunächst keine Asserts verwenden (zumindest nicht so, wie wir hier sprechen). Java hatte lange Zeit kein assert - Schlüsselwort und einige Sprachen immer noch nicht. Unit-Tests werden dann viel wichtiger. In einigen Sprachen Die Zusicherungen werden nur ausgeführt, wenn ein Flag gesetzt ist (erneut mit Java hier). Wenn die Schutzfunktionen nicht immer vorhanden sind, ist dies keine sehr nützliche Funktion.

Es gibt eine Denkschule, die besagt, dass Sie, wenn Sie etwas "behaupten", genauso gut einen aussagekräftigen Ausnahmeblock if/throw schreiben können. Dieser Denkprozess beruht auf vielen Behauptungen, die am Anfang einer Methode stehen, um sicherzustellen, dass alle Werte in Grenzen liegen. Das Testen Ihrer Voraussetzungen ist ein sehr wichtiger Bestandteil einer erwarteten Nachbedingung.

Unit-Tests sind zusätzlicher Code, der geschrieben und gepflegt werden muss. Für viele ist dies ein Nachteil. Mit der aktuellen Anzahl von Unit-Test-Frameworks können Sie jedoch eine größere Anzahl von Testbedingungen mit vergleichsweise wenig Code generieren. Parametrisierte Tests und "Theorien" führen denselben Test mit einer großen Anzahl von Datenproben durch, die einige schwer zu findende Fehler aufdecken können.

Ich persönlich finde, dass ich mit Unit-Tests mehr Kilometer bekomme als mit Sprinkler-Asserts, aber das liegt an den Plattformen, die ich die meiste Zeit entwickle (Java/C #). Andere Sprachen bieten eine robustere Assertion-Unterstützung und sogar "Design by Contract" (siehe unten), um noch mehr Garantien zu bieten. Wenn ich mit einer dieser Sprachen arbeiten würde, könnte ich den DBC mehr als den Unit-Test verwenden.

http://en.wikipedia.org/wiki/Design_by_contract#Languages_with_native_support

5
Berin Loritsch