Vertragsprogrammierung vs Unit Test


13

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.


6
Während Unit-Tests das Verschieben von Behauptungen aus dem Anwendungscode ermöglichen (und somit eine Überfrachtung vermeiden), können sie nicht alles überprüfen, was im realen Produktionssystem passieren kann. Daher bieten IMO-Codeverträge einige Vorteile, insbesondere für kritischen Code, bei dem die Korrektheit besonders wichtig ist.
Giorgio

Also ist es im Grunde genommen Entwicklungszeit, Wartbarkeit, Lesbarkeit im Vergleich zu einer besseren Codeabdeckung?
Ronag

1
Ich benutze meistens Assertion im Code, um die Richtigkeit der Parameter zu überprüfen (zum Beispiel auf Null), und im Unit-Test überprüfe ich zusätzlich diese Assertion.
Artjom

Antworten:


14

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.


2
Ich denke, es ist auch wichtig zu testen, ob der Code funktioniert (z. B. löst er eine Ausnahme aus), wenn die Bedingungen nicht erfüllt sind.
SVICK

6

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.


1

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.


0

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?


0

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.


0

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.