Ich bin ein großer Fan des Schreibens von assert
Prüfungen in C ++ - Code, um Fälle während der Entwicklung abzufangen, die möglicherweise nicht auftreten können, aber aufgrund von Logikfehlern in meinem Programm auftreten. Dies ist im Allgemeinen eine gute Praxis.
Ich habe jedoch festgestellt, dass einige von mir geschriebene Funktionen (die Teil einer komplexen Klasse sind) Asserts über 5 aufweisen, was hinsichtlich der Lesbarkeit und Wartbarkeit möglicherweise eine schlechte Programmierpraxis darstellt. Ich finde es immer noch großartig, da jeder von mir verlangt, über die Vor- und Nachbedingungen von Funktionen nachzudenken, und sie helfen wirklich dabei, Fehler zu finden. Ich wollte dies jedoch nur veröffentlichen, um zu fragen, ob es ein besseres Paradigma gibt, um logische Fehler zu erkennen, wenn eine große Anzahl von Überprüfungen erforderlich ist.
Emacs-Kommentar : Da Emacs meine bevorzugte IDE ist, sind die Assert-Anweisungen etwas grau hinterlegt, wodurch das Gefühl von Unordnung, das sie erzeugen können, verringert wird. Folgendes füge ich meiner .emacs-Datei hinzu:
; gray out the "assert(...)" wrapper
(add-hook 'c-mode-common-hook
(lambda () (font-lock-add-keywords nil
'(("\\<\\(assert\(.*\);\\)" 1 '(:foreground "#444444") t)))))
; gray out the stuff inside parenthesis with a slightly lighter color
(add-hook 'c-mode-common-hook
(lambda () (font-lock-add-keywords nil
'(("\\<assert\\(\(.*\);\\)" 1 '(:foreground "#666666") t)))))
quelle
Antworten:
Ich habe Hunderte von Fehlern gesehen, die schneller behoben worden wären, wenn jemand mehr Behauptungen geschrieben hätte, und keine einzige, die schneller behoben worden wäre, wenn weniger geschrieben worden wäre .
Vielleicht könnte die Lesbarkeit ein Problem sein - obwohl ich die Erfahrung gemacht habe, dass Leute, die gute Aussagen machen, auch lesbaren Code schreiben. Und es stört mich nie, wenn der Beginn einer Funktion mit einem Block von Zusicherungen beginnt, um sicherzustellen, dass die Argumente kein Müll sind - setzen Sie einfach eine leere Zeile dahinter.
Auch nach meiner Erfahrung wird die Wartbarkeit durch Asserts immer verbessert, genauso wie durch Unit-Tests. Asserts bieten eine Überprüfung, ob der Code so verwendet wird, wie er verwendet werden soll.
quelle
Nun, natürlich ist es das. [Stellen Sie sich hier ein abscheuliches Beispiel vor.] Wenn Sie jedoch die im Folgenden beschriebenen Richtlinien anwenden, sollten Sie in der Praxis keine Probleme haben, diese Grenze zu überschreiten. Ich bin auch ein großer Fan von Behauptungen und verwende sie nach diesen Grundsätzen. Ein Großteil dieser Ratschläge bezieht sich nicht auf Behauptungen, sondern nur auf die allgemein anerkannten Regeln der Technik.
Beachten Sie den Aufwand für Laufzeit und Binärdaten
Behauptungen sind großartig, aber wenn sie Ihr Programm inakzeptabel verlangsamen, wird es entweder sehr ärgerlich sein oder Sie werden sie früher oder später deaktivieren.
Ich möchte die Kosten einer Behauptung im Verhältnis zu den Kosten der Funktion messen, in der sie enthalten ist. Betrachten Sie die folgenden zwei Beispiele.
Die Funktion selbst ist eine O (1) -Operation, aber die Zusicherungen berücksichtigen den O ( n ) -Overhead. Ich glaube nicht, dass Sie möchten, dass solche Überprüfungen nur unter ganz besonderen Umständen durchgeführt werden.
Hier ist eine andere Funktion mit ähnlichen Behauptungen.
Die Funktion selbst ist eine O ( n ) -Operation, daher tut es viel weniger weh, einen zusätzlichen O ( n ) -Overhead für die Zusicherung hinzuzufügen . Eine Funktion um einen kleinen (in diesem Fall wahrscheinlich weniger als 3) konstanten Faktor zu verlangsamen, ist etwas, das wir uns normalerweise in einem Debug-Build leisten können, aber möglicherweise nicht in einem Release-Build.
Betrachten Sie nun dieses Beispiel.
Während sich viele Menschen mit dieser O (1) -Aussage wahrscheinlich viel wohler fühlen als mit den beiden O ( n ) -Aussagen im vorherigen Beispiel, sind sie meiner Ansicht nach moralisch gleichwertig. Jeder fügt Overhead in der Reihenfolge der Komplexität der Funktion selbst hinzu.
Schließlich gibt es die "wirklich billigen" Behauptungen, die von der Komplexität der Funktion, in der sie enthalten sind, dominiert werden.
Hier haben wir zwei O (1) -Aussagen in einer O ( n ) -Funktion. Es ist wahrscheinlich kein Problem, diesen Overhead auch in Release-Builds beizubehalten.
Bedenken Sie jedoch, dass asymptotische Komplexitäten nicht immer eine angemessene Schätzung liefern, da es sich in der Praxis immer um Eingabegrößen handelt, die durch einige endliche konstante und konstante Faktoren begrenzt sind, die von „Big- O “ verborgen werden .
Nun haben wir verschiedene Szenarien identifiziert. Was können wir dagegen tun? Ein (wahrscheinlich zu) einfacher Ansatz wäre die Befolgung einer Regel wie „Verwenden Sie keine Aussagen, die die Funktion dominieren, in der sie enthalten sind.“ Während dies für einige Projekte möglicherweise funktioniert, benötigen andere möglicherweise einen differenzierteren Ansatz. Dies könnte durch Verwendung verschiedener Zusicherungsmakros für die verschiedenen Fälle geschehen.
Sie können nun die drei Makros verwendet
MY_ASSERT_LOW
,MY_ASSERT_MEDIUM
undMY_ASSERT_HIGH
anstelle der Standard - Bibliothek „one size fits all“assert
Makro für Behauptungen , die von beherrscht werden, weder dominiert noch dominiert und die Komplexität ihrer Funktion enthält , dominiert sind. Beim Erstellen der Software können Sie das Präprozessorsymbol vordefinieren,MY_ASSERT_COST_LIMIT
um auszuwählen, welche Art von Zusicherungen es in die ausführbare Datei aufnehmen soll. Die KonstantenMY_ASSERT_COST_NONE
undMY_ASSERT_COST_ALL
entsprechen keinen Assert-Makros und dienen als Werte fürMY_ASSERT_COST_LIMIT
, um alle Assertions zu aktivieren bzw. zu deaktivieren.Wir gehen hier von der Annahme aus, dass ein guter Compiler keinen Code für generiert
und transformieren
in
was ich heutzutage für eine sichere Annahme halte.
Wenn Sie den obigen Code optimieren möchten, sollten Sie compilerspezifische Anmerkungen wie "
__attribute__ ((cold))
on"my::assertion_failed
oder "__builtin_expect(…, false)
on" berücksichtigen!(CONDITION)
, um den Aufwand für übergebene Zusicherungen zu verringern. In Release-Builds können Sie auch in Betracht ziehen, den Funktionsaufruf aufmy::assertion_failed
durch etwas__builtin_trap
zu ersetzen, das den Platzbedarf verringert , wenn Sie eine Diagnosemeldung verlieren.Diese Art von Optimierungen ist wirklich nur bei extrem billigen Zusicherungen (wie dem Vergleichen von zwei Ganzzahlen, die bereits als Argumente angegeben sind) in einer Funktion relevant, die selbst sehr kompakt ist, ohne die zusätzliche Größe der durch das Einbeziehen aller Nachrichtenzeichenfolgen akkumulierten Binärdatei zu berücksichtigen.
Vergleichen Sie, wie dieser Code
wird in die folgende Assembly kompiliert
während der folgende Code
gibt diese Versammlung
Womit ich mich viel wohler fühle. (Beispiele wurden mit GCC 5.3.0 getestet mit der
-std=c++14
,-O3
und-march=native
Fahnen auf 4.3.3-2-ARCH x86_64 GNU / Linux. Nicht in den oben genannten Schnipsel gezeigt sind die Erklärungentest::positive_difference_1st
undtest::positive_difference_2nd
die ich hinzugefügt die__attribute__ ((hot))
zu.my::assertion_failed
Erklärt wurde , mit__attribute__ ((cold))
.)Legen Sie in der von ihnen abhängigen Funktion die Voraussetzungen fest
Angenommen, Sie haben die folgende Funktion mit dem angegebenen Vertrag.
Anstatt zu schreiben
Schreiben Sie diese Logik an jeder Aufrufstelle einmal in die Definition von
count_letters
und nenne es ohne weiteres.
Dies hat folgende Vorteile.
assert
Anweisungen in Ihrem Code verringern .Der offensichtliche Nachteil ist, dass Sie den Quellort der Anrufstelle nicht in die Diagnosemeldung aufnehmen können. Ich glaube, dass dies ein kleines Problem ist. Ein guter Debugger sollte es Ihnen ermöglichen, die Ursache der Vertragsverletzung auf bequeme Weise nachzuvollziehen.
Dasselbe gilt für „spezielle“ Funktionen wie überladene Operatoren. Wenn ich Iteratoren schreibe, gebe ich ihnen normalerweise - wenn die Art des Iterators dies zulässt - eine Member-Funktion
das erlaubt zu fragen, ob es sicher ist, den Iterator zu dereferenzieren. (Natürlich kann in der Praxis fast immer nur garantiert werden, dass es nicht sicher ist, den Iterator zu dereferenzieren. Aber ich glaube, Sie können mit einer solchen Funktion immer noch viele Fehler finden.) Anstatt meinen gesamten Code zu verunreinigen Wenn der Iterator mit
assert(iter.good())
Anweisungen verwendet wird, möchte ich lieber eine einzelneassert(this->good())
Zeile als erste Zeileoperator*
in die Implementierung des Iterators einfügen.Wenn Sie die Standardbibliothek verwenden, müssen Sie deren Prüfungen in Debugbuilds aktivieren, anstatt die Voraussetzungen im Quellcode manuell festzulegen. Sie können noch komplexere Prüfungen durchführen, beispielsweise testen, ob der Container, auf den sich ein Iterator bezieht, noch vorhanden ist. ( Weitere Informationen finden Sie in der Dokumentation zu libstdc ++ und libc ++ (in Arbeit).)
Faktor gemeinsame Bedingungen aus
Angenommen, Sie schreiben ein lineares Algebra-Paket. Viele Funktionen haben komplizierte Voraussetzungen, und wenn sie verletzt werden, entstehen häufig falsche Ergebnisse, die nicht sofort als solche erkennbar sind. Es wäre sehr gut, wenn diese Funktionen ihre Voraussetzungen erfüllen würden. Wenn Sie eine Reihe von Prädikaten definieren, die Ihnen bestimmte Eigenschaften einer Struktur mitteilen, werden diese Aussagen viel besser lesbar.
Es werden auch nützlichere Fehlermeldungen angezeigt.
hilft viel mehr als sagen
Wo müsste man sich zuerst den Quellcode im Kontext ansehen, um herauszufinden, was tatsächlich getestet wurde.
Wenn Sie eine
class
mit nicht-trivialen Invarianten haben, ist es wahrscheinlich eine gute Idee, diese von Zeit zu Zeit zu bestätigen, wenn Sie mit dem internen Status in Konflikt geraten sind und sicherstellen möchten, dass Sie das Objekt bei der Rückkehr in einem gültigen Status belassen.Zu diesem Zweck fand ich es nützlich, eine
private
Member-Funktion zu definieren , die ich herkömmlicherweise aufrufeclass_invaraiants_hold_
. Angenommenstd::vector
, Sie haben eine Neuimplementierung durchgeführt (da wir alle wissen, dass sie nicht gut genug ist), könnte sie eine Funktion wie diese haben.Beachten Sie ein paar Dinge dazu.
const
undnoexcept
gemäß der Richtlinie, dass Aussagen keine Nebenwirkungen haben dürfen. Wenn es sinnvoll ist, erklären Sie es auchconstexpr
.assert(this->class_invariants_hold_())
. Auf diese Weise können wir sicher sein, dass bei der Kompilierung von Zusicherungen kein Laufzeit-Overhead anfällt.if
Anweisungen mit einem frühenreturn
s und keinem großen Ausdruck unterteilt. Dies macht es einfach, die Funktion in einem Debugger zu durchlaufen und herauszufinden, welcher Teil der Invariante beschädigt war, wenn die Behauptung ausgelöst wurde.Behaupte nicht dumme Dinge
Manche Dinge machen einfach keinen Sinn, sich zu behaupten.
Diese Behauptungen machen den Code nicht einmal ein bisschen lesbarer oder einfacher zu überlegen. Jeder C ++ - Programmierer sollte sicher genug sein, wie es
std::vector
funktioniert, um sicherzugehen, dass der obige Code korrekt ist, wenn er nur angeschaut wird. Ich sage nicht, dass Sie sich niemals auf die Größe eines Containers festlegen sollten. Wenn Sie Elemente mithilfe eines nicht trivialen Kontrollflusses hinzugefügt oder entfernt haben, kann eine solche Behauptung hilfreich sein. Wenn jedoch nur wiederholt wird, was in dem oben beschriebenen Nicht-Assertions-Code geschrieben wurde, wird kein Wert gewonnen.Stellen Sie auch nicht sicher, dass die Bibliotheksfunktionen korrekt funktionieren.
Wenn Sie der Bibliothek so wenig vertrauen, sollten Sie stattdessen eine andere Bibliothek verwenden.
Wenn andererseits die Dokumentation der Bibliothek nicht 100% eindeutig ist und Sie durch Lesen des Quellcodes Vertrauen in ihre Verträge gewinnen, ist es sehr sinnvoll, auf diesen „abgeleiteten Vertrag“ zu vertrauen. Wenn es in einer zukünftigen Version der Bibliothek kaputt geht, werden Sie es schnell bemerken.
Dies ist besser als die folgende Lösung, die Ihnen nicht sagt, ob Ihre Annahmen korrekt waren.
Behauptungen nicht missbrauchen, um Programmlogik zu implementieren
Behauptungen sollten immer nur verwendet werden, um Fehler aufzudecken , die es wert sind, Ihre Anwendung sofort zu beenden. Sie sollten nicht verwendet werden, um andere Zustände zu verifizieren, selbst wenn die entsprechende Reaktion auf diesen Zustand auch darin bestehen würde, sofort zu beenden.
Schreiben Sie deshalb folgendes…
…stattdessen.
Auch niemals Behauptungen verwenden , um nicht vertrauenswürdige Eingaben zu bestätigen oder überprüfen, ob
std::malloc
tat nichtreturn
Sie dienullptr
. Selbst wenn Sie wissen, dass Sie Behauptungen selbst in Release-Builds niemals deaktivieren werden, teilt eine Behauptung dem Leser mit, dass sie etwas überprüft, das immer wahr ist, vorausgesetzt, das Programm ist fehlerfrei und hat keine sichtbaren Nebenwirkungen. Wenn dies nicht die Art von Nachricht ist, die Sie kommunizieren möchten, verwenden Sie einen alternativen Fehlerbehandlungsmechanismus, z. B.throw
eine Ausnahme. Wenn Sie es bequem finden, einen Makro-Wrapper für Ihre Nicht-Assertions-Checks zu haben, schreiben Sie einen. Nennen Sie es einfach nicht "behaupten", "annehmen", "verlangen", "sicherstellen" oder so ähnlich. Ihre interne Logik könnte dieselbe sein wie fürassert
, außer dass sie natürlich niemals kompiliert wird.Mehr Informationen
Ich fand die Rede John Lakos Defensive Programmierung Done Right , da bei CppCon'14 ( 1 st Teil , 2 nd Teil sehr) erleuchtet. Er hat die Idee, die aktivierten Behauptungen anzupassen und auf fehlgeschlagene Ausnahmen noch weiter zu reagieren als in dieser Antwort.
quelle
Assertions are great, but ... you will turn them off sooner or later.
- Hoffentlich früher, wie vor dem Versand des Codes. Dinge, die das Programm in der Produktion zum Sterben bringen müssen, sollten Teil des "echten" Codes sein, nicht in Behauptungen.Ich finde, dass ich im Laufe der Zeit weniger Asserts schreibe, weil viele von ihnen "der Compiler arbeitet" und "die Bibliothek arbeitet" bedeuten. Wenn Sie erst einmal darüber nachdenken, was genau Sie testen, werden Sie vermutlich weniger Asserts schreiben.
Zum Beispiel sollte eine Methode, die einer Sammlung etwas hinzufügt, nicht behaupten müssen, dass die Sammlung vorhanden ist - dies ist im Allgemeinen entweder eine Voraussetzung für die Klasse, der die Nachricht gehört, oder ein schwerwiegender Fehler, der sie an den Benutzer zurückgeben sollte . Überprüfen Sie es also einmal, sehr früh, und nehmen Sie es dann an.
Behauptungen sind für mich ein Debugging-Tool, und ich werde sie im Allgemeinen auf zwei Arten verwenden: Auffinden eines Fehlers an meinem Schreibtisch (und sie werden nicht eingecheckt. Nun, vielleicht der eine Schlüssel, den man einchecken könnte); und einen Fehler auf dem Schreibtisch des Kunden zu finden (und sie werden eingecheckt). In beiden Fällen verwende ich Assertions hauptsächlich, um einen Stack-Trace zu generieren, nachdem eine Ausnahme so früh wie möglich erzwungen wurde. Beachten Sie, dass Assertions, die auf diese Weise verwendet werden, leicht zu Heisenbugs führen können - der Fehler kann durchaus nie in der Debug-Erstellung auftreten, für die Assertions aktiviert sind.
quelle
Zu wenige Behauptungen: Viel Glück beim Ändern dieses Codes, der mit versteckten Annahmen behaftet ist.
Zu viele Behauptungen: Kann zu Lesbarkeitsproblemen und möglicherweise zu Codegerüchen führen. Wurden Klasse, Funktion und API richtig entwickelt, wenn so viele Annahmen in Behauptungsanweisungen enthalten sind?
Es könnte auch Assertions geben, die in jeder Funktion nichts wirklich überprüfen oder Dinge wie Compilereinstellungen überprüfen: /
Streben Sie den Sweet Spot an, aber nicht weniger (wie bereits jemand anderes gesagt hat, ist "mehr" von Behauptungen weniger schädlich als wenn zu wenige oder Gott uns helfen - keine).
quelle
Es wäre fantastisch, wenn Sie eine Assert-Funktion schreiben könnten, die nur auf eine boolesche CONST-Methode verweist. Auf diese Weise können Sie sicher sein, dass Ihre Asserts keine Nebenwirkungen haben, indem Sie sicherstellen, dass zum Testen der Assert-Methode eine boolesche const-Methode verwendet wird
Es würde ein wenig von der Lesbarkeit abhängen, zumal ich nicht glaube, dass Sie ein Lambda (in c ++ 0x) nicht mit Anmerkungen versehen können, um eine Konstante für eine Klasse zu sein, was bedeutet, dass Sie dafür keine Lambdas verwenden können
Overkill, wenn Sie mich fragen, aber wenn ich aufgrund von Behauptungen anfangen würde, ein gewisses Maß an Verschmutzung zu sehen, wäre ich in Bezug auf zwei Dinge vorsichtig:
quelle
Ich habe viel mehr in C # geschrieben als in C ++, aber die beiden Sprachen sind nicht sehr weit voneinander entfernt. In .Net verwende ich Asserts für Bedingungen, die nicht auftreten sollten, aber ich mache auch oft Ausnahmen, wenn es keine Möglichkeit gibt, fortzufahren. Der VS2010-Debugger zeigt mir viele gute Informationen zu einer Ausnahme, egal wie optimiert der Release-Build ist. Es ist auch eine gute Idee, Unit-Tests hinzuzufügen, wenn Sie können. Manchmal ist die Protokollierung auch eine gute Debug-Hilfe.
Kann es also zu viele Behauptungen geben? Ja. Die Wahl zwischen Abbrechen / Ignorieren / Fortsetzen 15 Mal in einer Minute wird ärgerlich. Eine Ausnahme wird nur einmal ausgelöst. Es ist schwer zu quantifizieren, an welchem Punkt zu viele Zusicherungen vorliegen, aber wenn Ihre Zusicherungen die Rolle von Zusicherungen, Ausnahmen, Komponententests und Protokollierung erfüllen, stimmt etwas nicht.
Ich würde Behauptungen für die Szenarien behalten, die nicht passieren sollten. Sie können anfangs zu viel behaupten, weil Zusicherungen schneller zu schreiben sind, aber den Code später neu faktorisieren - einige davon in Ausnahmen, andere in Tests usw. verwandeln Kommentar neben jedem, den Sie überarbeiten möchten, und NICHT VERGESSEN, das TODO später anzusprechen.
quelle
Ich möchte mit dir arbeiten! Jemand, der viel schreibt,
asserts
ist fantastisch. Ich weiß nicht, ob es so etwas wie "zu viele" gibt. Weitaus häufiger sind für mich Leute, die zu wenig schreiben und letztendlich auf die gelegentliche tödliche UB-Ausgabe stoßen, die nur auf einem Vollmond auftaucht, der sich mit einem simplen leicht wiederholt hätte reproduzieren lassenassert
.Fehlermeldung
Die eine Sache, an die ich denken kann, ist, Fehlerinformationen in die einzubetten,
assert
wenn Sie es nicht bereits tun, wie folgt :Auf diese Weise haben Sie möglicherweise nicht mehr das Gefühl, zu viele zu haben, wenn Sie dies nicht bereits getan haben, da Sie jetzt Ihre Aussagen dazu bringen, eine stärkere Rolle bei der Dokumentation von Annahmen und Voraussetzungen zu spielen.
Nebenwirkungen
Natürlich
assert
kann man das auch tatsächlich missbrauchen und so Fehler einbringen:... wenn es zu
foo()
Nebenwirkungen kommt, sollten Sie diesbezüglich sehr vorsichtig sein, aber ich bin sicher, Sie sind bereits einer, der sehr liberal behauptet (ein "erfahrener Asserter"). Hoffentlich ist Ihr Testverfahren auch so gut wie Ihre sorgfältige Beachtung von Annahmen.Debugging-Geschwindigkeit
Während die Geschwindigkeit des Debuggens im Allgemeinen ganz unten auf unserer Prioritätenliste stehen sollte, habe ich einmal so viel in einer Codebasis behauptet, bevor der Debug-Build über den Debugger mehr als 100-mal langsamer als die Veröffentlichung war.
Das lag hauptsächlich daran, dass ich Funktionen wie diese hatte:
... bei dem jeder einzelne Anruf zu
operator[]
einer Überprüfung der Grenzen führen würde. Am Ende ersetzte ich einige der leistungskritischen durch unsichere Entsprechungen, die nicht nur das Debugging-Build drastisch beschleunigen, sondern auch die Sicherheit auf Implementierungsdetailebene beeinträchtigen und nur deshalb, weil der Geschwindigkeitsschub einsetzte Dies führt zu einer spürbaren Produktivitätsverschlechterung (der Vorteil eines schnelleren Debuggens überwiegt die Kosten für den Verlust einiger weniger Asserts, jedoch nur für Funktionen wie diese produktübergreifende Funktion, die auf den kritischsten, gemessenen Pfaden verwendet wurde, nicht füroperator[]
im Allgemeinen).Grundsatz der einheitlichen Verantwortung
Ich glaube zwar nicht, dass Sie mit mehr Behauptungen wirklich etwas falsch machen können (zumindest ist es weitaus besser, sich auf die Seite von zu vielen als auf die von zu wenigen zu setzen), aber die Behauptungen selbst sind möglicherweise kein Problem, sondern weisen möglicherweise auf eine hin.
Wenn Sie beispielsweise 5 Zusicherungen für einen einzelnen Funktionsaufruf haben, kann dies zu viel bewirken. Die Schnittstelle hat möglicherweise zu viele Vorbedingungen und Eingabeparameter. Ich halte dies beispielsweise nicht für das Thema, aus dem sich eine gesunde Anzahl von Behauptungen zusammensetzt (auf die ich im Allgemeinen antworten würde: "Je mehr, desto besser!"), Aber das könnte sein eine mögliche rote Fahne (oder sehr wahrscheinlich nicht).
quelle
Es ist sehr vernünftig, Ihrem Code Überprüfungen hinzuzufügen. Für einfache Zusicherungen (die in C und C ++ Compiler integriert sind) ist mein Verwendungsmuster, dass eine fehlgeschlagene Zusicherung bedeutet, dass es einen Fehler im Code gibt, der behoben werden muss. Ich interpretiere das ein bisschen großzügig; Wenn ich erwarte, dass eine Webanforderung den Status 200 zurückgibt und ohne Bearbeitung anderer Fälle darauf hinweist, zeigt eine fehlgeschlagene Zusicherung tatsächlich einen Fehler in meinem Code, sodass die Zusicherung gerechtfertigt ist.
Wenn also Leute behaupten, dass nur überprüft wird, was der Code tut, ist das überflüssig, was nicht ganz richtig ist. Diese Zusicherung überprüft, was der Code ihrer Meinung nach tut, und der Sinn der Zusicherung besteht darin, zu überprüfen, ob die Annahme, dass kein Fehler im Code vorliegt, richtig ist. Und die Behauptung kann auch als Dokumentation dienen. Wenn ich davon ausgehe, dass nach dem Ausführen einer Schleife i == n und es nicht zu 100% aus dem Code ersichtlich ist, ist "assert (i == n)" hilfreich.
Es ist besser, mehr als nur "Behauptung" in Ihrem Repertoire zu haben, um mit unterschiedlichen Situationen umzugehen. Zum Beispiel die Situation, in der ich überprüfe, dass etwas nicht passiert, was auf einen Fehler hindeutet, aber trotzdem weiter daran arbeite. (Wenn ich beispielsweise einen Cache verwende, prüfe ich möglicherweise, ob Fehler vorliegen. Wenn ein Fehler unerwartet auftritt, kann er möglicherweise sicher behoben werden, indem der Cache weggeworfen wird. Ich möchte etwas, das beinahe eine Bestätigung darstellt und mir während der Entwicklung mitteilt und lass mich trotzdem weitermachen.
Ein anderes Beispiel ist die Situation, in der ich nicht erwarte, dass etwas passiert. Ich habe eine allgemeine Problemumgehung, aber wenn dies passiert, möchte ich etwas darüber wissen und es untersuchen. Wieder so etwas wie eine Behauptung, die mir während der Entwicklung sagen sollte. Aber nicht ganz eine Behauptung.
Zu viele Asserts: Wenn ein Assert Ihr Programm zum Absturz bringt, während es sich in den Händen des Benutzers befindet, dürfen Sie keine Asserts haben, die wegen falscher Negative abstürzen.
quelle
Es hängt davon ab, ob. Wenn die Codeanforderungen eindeutig dokumentiert sind, sollte die Zusicherung immer mit den Anforderungen übereinstimmen. In diesem Fall ist es eine gute Sache. Wenn es jedoch keine oder schlecht geschriebene Anforderungen gibt, ist es für neue Programmierer schwierig, Code zu bearbeiten, ohne jedes Mal den Komponententest durchlaufen zu müssen, um die Anforderungen zu ermitteln.
quelle