Ich habe in letzter Zeit ein bisschen über Literate Programming gelesen und dachte darüber nach ... Gut geschriebene Tests, insbesondere BDD-Spezifikationen, können besser erklären, was Code tut als Prosa und haben den großen Vorteil von Überprüfung ihrer eigenen Genauigkeit.
Ich habe noch nie Tests gesehen, die in den Code geschrieben wurden, den sie testen. Liegt dies nur daran, dass Sprachen es nicht einfach machen, Anwendungen und Testcodes in derselben Quelldatei zu trennen (und niemand hat es leicht gemacht), oder gibt es einen eher grundsätzlichen Grund, warum Benutzer Testcode und Anwendungscode trennen?
testing
unit-testing
bdd
literate-programming
Chris Devereux
quelle
quelle
Antworten:
Der einzige Vorteil, den ich mir für Inline-Tests vorstellen kann, ist die Reduzierung der Anzahl der zu schreibenden Dateien. Bei modernen IDEs ist dies keine so große Sache.
Es gibt jedoch eine Reihe offensichtlicher Nachteile beim Inline-Testen:
quelle
Ich kann mir einige vorstellen:
Lesbarkeit. Das Einstreuen von "echtem" Code und Tests erschwert das Lesen des echten Codes.
Code aufblähen. Das Mischen von "echtem" Code und Testcode in dieselben Dateien / Klassen / was auch immer führt wahrscheinlich zu größeren kompilierten Dateien usw. Dies ist besonders wichtig für Sprachen mit später Bindung.
Möglicherweise möchten Sie nicht, dass Ihre Kunden / Kunden Ihren Testcode sehen. (Ich mag diesen Grund nicht ... aber wenn Sie an einem Closed-Source-Projekt arbeiten, ist es unwahrscheinlich, dass der Testcode dem Kunden trotzdem hilft.)
Jetzt gibt es mögliche Problemumgehungen für jedes dieser Probleme. Aber IMO, es ist einfacher, überhaupt nicht dorthin zu gehen.
Es ist erwähnenswert, dass Java-Programmierer in der Anfangszeit solche Dinge taten. zB Einbeziehen einer
main(...)
Methode in eine Klasse, um das Testen zu erleichtern. Diese Idee ist fast vollständig verschwunden. Es ist in der Industrie üblich, Tests separat mit einem Test-Framework durchzuführen.Es ist auch erwähnenswert, dass die literarische Programmierung (wie von Knuth konzipiert) in der Softwareentwicklungsbranche noch nie Fuß gefasst hat.
quelle
Eigentlich können Sie sich Design By Contract so vorstellen. Das Problem ist, dass die meisten Programmiersprachen nicht zulassen, dass Sie Code wie folgt schreiben :( Es ist sehr einfach, die Voraussetzungen manuell zu testen, aber die Post-Bedingungen sind eine echte Herausforderung, ohne die Art und Weise zu ändern, in der Sie Code schreiben (eine riesige negative IMO).
Michael Feathers hat eine Präsentation darüber und dies ist eine der vielen Möglichkeiten, wie Sie die Codequalität verbessern können.
quelle
Aus den gleichen Gründen, aus denen Sie versuchen, eine enge Kopplung zwischen Klassen in Ihrem Code zu vermeiden, sollten Sie auch eine unnötige Kopplung zwischen Tests und Code vermeiden.
Erstellung: Tests und Code können zu unterschiedlichen Zeiten von unterschiedlichen Personen geschrieben werden.
Kontrolle: Wenn Tests verwendet werden, um Anforderungen zu spezifizieren, möchten Sie sicher, dass sie unterschiedlichen Regeln unterliegen, die festlegen, wer sie wann ändern kann.
Wiederverwendbarkeit: Wenn Sie die Tests inline setzen, können Sie sie nicht mit einem anderen Codeteil verwenden.
Stellen Sie sich vor, Sie haben einen Teil des Codes, der die Aufgabe korrekt erledigt, aber in Bezug auf Leistung, Wartbarkeit und was auch immer zu wünschen übrig lässt. Sie beschließen, diesen Code durch neuen und verbesserten Code zu ersetzen. Mit denselben Tests können Sie überprüfen, ob der neue Code dieselben Ergebnisse wie der alte Code liefert.
Auswählbarkeit: Wenn Sie die Tests vom Code trennen, können Sie leichter auswählen, welche Tests Sie ausführen möchten.
Beispielsweise verfügen Sie möglicherweise über eine kleine Testsuite, die sich nur auf den Code bezieht, an dem Sie gerade arbeiten, und eine größere Suite, die das gesamte Projekt testet.
quelle
Hier sind einige zusätzliche Gründe, die mir einfallen:
Tests in einer separaten Bibliothek zu haben, macht es einfacher, nur diese Bibliothek mit Ihrem Testframework und nicht mit Ihrem Produktionscode zu verknüpfen (dies könnte von einem Vorprozessor vermieden werden, aber warum sollte man so etwas erstellen, wenn die einfachere Lösung darin besteht, die Tests einzuschreiben? ein separater Ort)
Tests einer Funktion, einer Klasse oder einer Bibliothek werden normalerweise unter dem Gesichtspunkt "Benutzer" (ein Benutzer dieser Funktion / Klasse / Bibliothek) geschrieben. Solch ein "Verwenden von Code" wird normalerweise in eine separate Datei oder Bibliothek geschrieben, und ein Test kann klarer oder "realistischer" sein, wenn er diese Situation nachahmt.
quelle
Wenn die Tests inline wären, müssten Sie den Code entfernen, den Sie zum Testen benötigen, wenn Sie das Produkt an Ihren Kunden senden. Ein zusätzlicher Ort, an dem Sie Ihre Tests speichern, trennt einfach zwischen dem Code, den Sie benötigen, und dem Code, den Ihr Kunde benötigt.
quelle
Diese Idee läuft einfach auf eine "Self_Test" -Methode im Kontext eines objektbasierten oder objektorientierten Designs hinaus. Wenn Sie eine kompilierte objektbasierte Sprache wie Ada verwenden, wird der gesamte Selbsttestcode während der Produktionskompilierung vom Compiler als nicht verwendet (nie aufgerufen) markiert und daher alles wegoptimiert - nichts davon erscheint in der resultierende ausführbare Datei.
Die Verwendung einer "Self_Test" -Methode ist eine sehr gute Idee, und wenn Programmierer wirklich auf Qualität bedacht wären, würden sie es alle tun. Ein wichtiges Problem ist jedoch, dass die Methode "Self_Test" eine intensive Disziplin haben muss, da sie nicht auf Implementierungsdetails zugreifen kann und stattdessen nur auf alle anderen veröffentlichten Methoden innerhalb der Objektspezifikation angewiesen ist. Wenn der Selbsttest fehlschlägt, muss sich die Implementierung natürlich ändern. Der Selbsttest sollte streng alle veröffentlichten Eigenschaften der Objektmethoden testen, sich jedoch niemals auf Details einer bestimmten Implementierung verlassen.
Objektbasierte und objektorientierte Sprachen bieten häufig genau diese Art von Disziplin in Bezug auf Methoden außerhalb des getesteten Objekts (sie erzwingen die Objektspezifikation, verhindern den Zugriff auf die Implementierungsdetails und verursachen einen Kompilierungsfehler, wenn ein solcher Versuch entdeckt wird ). Alle internen Methoden des Objekts erhalten jedoch vollständigen Zugriff auf alle Implementierungsdetails. Die Selbsttestmethode befindet sich in einer einzigartigen Situation: Sie muss aufgrund ihrer Natur eine interne Methode sein (Selbsttest ist offensichtlich eine Methode des zu testenden Objekts), muss jedoch die gesamte Compiler-Disziplin einer externen Methode enthalten ( Es muss unabhängig von den Implementierungsdetails des Objekts sein. Nur wenige Programmiersprachen bieten die Möglichkeit, ein Objekt zu disziplinieren. “ s interne Methode als wäre es eine externe Methode. Dies ist also ein wichtiges Problem beim Design von Programmiersprachen.
Wenn die Programmiersprache nicht ordnungsgemäß unterstützt wird, erstellen Sie am besten ein Begleitobjekt. Mit anderen Worten, für jedes Objekt, das Sie codieren (nennen wir es "Big_Object"), erstellen Sie auch ein zweites Begleitobjekt, dessen Name aus einem Standardsuffix besteht, das mit dem Namen des "echten" Objekts verknüpft ist (in diesem Fall "Big_Object_Self_Test") ") und deren Spezifikation aus einer einzelnen Methode besteht (" Big_Object_Self_Test.Self_Test (This_Big_Object: Big_Object) return Boolean; "). Das Begleitobjekt hängt dann von der Spezifikation des Hauptobjekts ab, und der Compiler setzt die gesamte Disziplin dieser Spezifikation vollständig gegen die Implementierung des Begleitobjekts durch.
quelle
Dies ist eine Reaktion auf eine große Anzahl von Kommentaren, die darauf hindeuten, dass Inline-Tests nicht durchgeführt werden, da es schwierig bis unmöglich ist, den Testcode aus Release-Builds zu entfernen. Das ist falsch. Nahezu alle Compiler und Assembler unterstützen dies bereits, bei kompilierten Sprachen wie C, C ++, C # geschieht dies mit sogenannten Compiler-Direktiven.
Im Fall von c # (ich glaube auch, dass die Syntax in c ++ je nachdem, welchen Compiler Sie verwenden, leicht unterschiedlich sein kann) können Sie dies auf diese Weise tun.
Da dies Compiler-Direktiven verwendet, ist der Code in den ausführbaren Dateien, die erstellt werden, nicht vorhanden, wenn die Flags nicht gesetzt sind. Auf diese Weise erstellen Sie auch Programme zum einmaligen Schreiben und zum zweimaligen Kompilieren für mehrere Plattformen / Hardware.
quelle
Wir verwenden Inline-Tests mit unserem Perl-Code. Es gibt ein Modul, Test :: Inline , das aus dem Inline-Code Testdateien generiert.
Ich bin nicht besonders gut darin, meine Tests zu organisieren, und habe festgestellt, dass sie einfacher sind und eher gewartet werden, wenn sie inline sind.
Als Antwort auf einige der angesprochenen Bedenken:
+-- 33 lines: #test----
. Wenn Sie mit dem Test arbeiten möchten, erweitern Sie ihn einfach.Als Referenz:
quelle
Erlang 2 unterstützt tatsächlich Inline-Tests. Jeder boolesche Ausdruck im Code, der nicht verwendet wird (z. B. einer Variablen zugewiesen oder übergeben), wird automatisch als Test behandelt und vom Compiler ausgewertet. Wenn der Ausdruck falsch ist, wird der Code nicht kompiliert.
quelle
Ein weiterer Grund für die Trennung von Tests ist, dass Sie häufig zusätzliche oder sogar andere Bibliotheken zum Testen verwenden als für die eigentliche Implementierung. Wenn Sie Tests und Implementierung mischen, kann der Compiler die versehentliche Verwendung von Testbibliotheken in der Implementierung nicht abfangen.
Außerdem enthalten Tests in der Regel viel mehr Codezeilen als die von ihnen getesteten Implementierungsteile, sodass Sie zwischen allen Tests Schwierigkeiten haben, die Implementierung zu finden. :-)
quelle
Das stimmt nicht. Es ist viel besser, Ihre Unit-Tests neben dem Produktionscode zu platzieren, wenn der Produktionscode rein ist, insbesondere wenn die Produktionsroutine rein ist.
Wenn Sie beispielsweise unter .NET entwickeln, können Sie Ihren Testcode in die Produktionsassembly einfügen und diese vor dem Versand mit Scalpel entfernen.
quelle