Ich bin ein etwas defensiver Programmierer und ein großer Fan von Microsoft Code Contracts.
Jetzt kann ich nicht immer C # verwenden und in den meisten Sprachen habe ich nur noch Assertions. So lande ich normalerweise mit folgendem Code:
class
{
function()
{
checkInvariants();
assert(/* requirement */);
try
{
/* implementation */
}
catch(...)
{
assert(/* exceptional ensures */);
}
finally
{
assert(/* ensures */);
checkInvariants();
}
}
void checkInvariants()
{
assert(/* invariant */);
}
}
Dieses Paradigma (oder wie auch immer Sie es nennen würden) führt jedoch zu viel Code-Unordnung.
Ich habe angefangen mich zu fragen, ob es sich wirklich lohnt und ob ein richtiger Unit-Test dies bereits abdecken würde.
testing
code-contracts
Ronag
quelle
quelle
Antworten:
Ich denke nicht, dass Sie es als "vs" betrachten sollten.
Wie in den Kommentaren von @Giorgio erwähnt, dienen Codeverträge zum Überprüfen von Invarianten (in der Produktionsumgebung) und Unit-Tests zum Sicherstellen, dass der Code unter diesen Bedingungen erwartungsgemäß funktioniert.
quelle
Verträge helfen Ihnen mit mindestens einer Sache, die Unit-Tests nicht tun. Wenn Sie eine öffentliche API entwickeln, können Sie nicht testen, wie Benutzer Ihren Code verwenden. Sie können jedoch weiterhin Verträge für Ihre Methoden definieren.
Ich persönlich würde Verträge nur dann so rigoros angehen, wenn ich mich mit der öffentlichen API eines Moduls befasse. In vielen anderen Fällen ist es wahrscheinlich nicht die Mühe wert (und Sie können stattdessen Unit-Tests verwenden), aber dies ist nur meine Meinung.
Das heißt nicht, dass ich in solchen Fällen raten sollte, nicht an Verträge zu denken. Ich denke immer an sie. Ich halte es einfach nicht für notwendig, sie immer explizit zu codieren.
quelle
Wie bereits erwähnt, haben Verträge und Unit-Tests unterschiedliche Zwecke.
Bei Verträgen geht es um defensive Programmierung, um sicherzustellen, dass die Voraussetzungen erfüllt sind, Code mit den richtigen Parametern aufgerufen wird usw.
Unit-Tests, um sicherzustellen, dass der Code in verschiedenen Szenarien funktioniert. Das sind wie "Brillen mit Zähnen".
Asserts sind gut, sie machen Code robust. Wenn Sie jedoch befürchten, dass viel Code hinzugefügt wird, möchten Sie möglicherweise auch an einigen Stellen während des Debuggens bedingte Haltepunkte hinzufügen und die Anzahl der Asserts verringern.
quelle
Alles, was Sie in Ihren checkVariants () - Aufrufen haben, könnte durch Testen erledigt werden. Wie viel Aufwand dies tatsächlich sein könnte, hängt von vielen Dingen ab (externe Abhängigkeiten, Kopplungsebenen usw.), aber es würde den Code aus einer Sicht bereinigen. Ich bin mir nicht sicher, wie prüfbar eine gegen Behauptungen entwickelte Codebasis wäre, wenn sie nicht überarbeitet würde.
Ich stimme @duros zu, diese Ansätze sollten nicht als exklusiv oder konkurrierend angesehen werden. Tatsächlich könnte man in einer TDD-Umgebung sogar argumentieren, dass die "Anforderungs" -Aussagen Tests erfordern würden :).
Asserts machen Code jedoch NICHT robuster, es sei denn, Sie beheben die fehlgeschlagene Prüfung. Sie verhindern lediglich, dass Daten beschädigt werden oder ähnliches, in der Regel indem die Verarbeitung beim ersten Anzeichen von Problemen abgebrochen wird.
Eine testgetriebene / gut getestete Lösung hat in der Regel bereits bei der Entwicklung der interagierenden Komponenten über viele der Ursachen / Ursachen für fehlerhafte Ein- und Ausgaben nachgedacht und / oder nachgedacht und diese bereits näher an der Ursache des Problems behandelt.
Wenn Ihre Quelle extern ist und Sie keine Kontrolle darüber haben, sollten Sie eine Art Datenbereinigungs- / Zusicherungskomponente zwischen der Quelle und Ihrer Komponente implementieren, um zu vermeiden, dass Ihr Code durch andere Codeprobleme unübersichtlich wird, und Ihre Überprüfungen dort vornehmen .
Ich bin auch neugierig, welche Sprachen Sie verwenden, die keine xUnit- oder andere Testbibliothek haben, die jemand entwickelt hat. Ich dachte, es gibt heutzutage so ziemlich für alles etwas?
quelle
Neben Unit-Tests und Code-Verträgen möchte ich noch einen weiteren Aspekt hervorheben, nämlich den Wert, der für die Definition Ihrer Schnittstellen erforderlich ist, damit Sie die Möglichkeit eliminieren oder verringern, dass Ihr Code überhaupt nicht korrekt aufgerufen wird.
Es ist nicht immer einfach oder möglich, aber es lohnt sich auf jeden Fall, sich die Frage zu stellen: "Kann ich diesen Code narrensicherer machen?"
Anders Hejlsberg, Erfinder von C #, sagte, dass einer der größten Fehler in C # darin bestand, nicht nullbare Referenztypen nicht einzuschließen. Dies ist einer der Hauptgründe dafür, dass so viel Sicherheitscode-Unordnung vorhanden ist.
Das Refactoring, um nur die erforderliche und ausreichende Menge an Guard-Code zu haben, führt meiner Erfahrung nach zu einem benutzerfreundlicheren und wartbareren Code.
Stimmen Sie im Übrigen mit @duros überein.
quelle
Tun Sie beides, aber machen Sie einige statische Hilfsmethoden, um Ihre Absichten zu klären. Dies hat Google für Java getan. Lesen Sie dazu code.google.com/p/guava-libraries/wiki/PreconditionsExplained
quelle