Ich schreibe einen Parser und als Teil davon habe ich eine Expander
Klasse, die eine einzelne komplexe Anweisung in mehrere einfache Anweisungen "erweitert". Zum Beispiel würde es dies erweitern:
x = 2 + 3 * a
in:
tmp1 = 3 * a
x = 2 + tmp1
Jetzt überlege ich, wie ich diese Klasse testen soll, insbesondere, wie ich die Tests anordnen soll. Ich könnte den Eingabesyntaxbaum manuell erstellen:
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
Oder ich könnte es als String schreiben und analysieren:
var input = new Parser().ParseStatement("x = 2 + 3 * a");
Die zweite Option ist viel einfacher, kürzer und lesbar. Es führt aber auch eine Abhängigkeit von ein Parser
, was bedeutet, dass ein Fehler in Parser
diesem Test fehlschlagen könnte. Der Test würde also aufhören, ein Komponententest von Expander
und zu sein, und ich denke, er wird technisch zu einem Integrationstest von Parser
und Expander
.
Meine Frage ist: Ist es in Ordnung, sich zum Testen dieser Expander
Klasse größtenteils (oder vollständig) auf diese Art von Integrationstests zu verlassen ?
Parser
einen anderen Test nicht bestehen kann, ist kein Problem, wenn Sie gewohnheitsmäßig nur bei null Fehlern einen Fehler begehenParser
. Ich würde mir eher Sorgen machen, dass ein FehlerParser
diesen Test zum Erfolg führen könnte, wenn er fehlschlagen sollte . Unit-Tests sind schließlich da, um Fehler zu finden - ein Test ist kaputt, wenn er es nicht tut, aber sollte.Antworten:
Sie werden feststellen, dass Sie viel mehr Tests schreiben, die viel komplizierter, interessanter und nützlicher sind, wenn Sie dies einfach tun können. Also die Option, die beinhaltet
ist durchaus gültig. Es hängt von einer anderen Komponente ab. Aber alles hängt von Dutzenden anderer Komponenten ab. Wenn Sie etwas auf einen Zentimeter genau verspotten, sind Sie wahrscheinlich auf viele Verspottungsfunktionen und Testgeräte angewiesen.
Entwickler manchmal über Fokus auf der Reinheit ihrer Komponententests oder Tests und Entwicklungseinheit Komponententests nur ohne Modul, Integration, Stress oder andere Arten von Tests. Alle diese Formulare sind gültig und nützlich, und sie liegen in der Verantwortung der Entwickler - nicht nur der Q / A-Mitarbeiter oder des Betriebspersonals weiter unten in der Pipeline.
Ein Ansatz, den ich verwendet habe, besteht darin, mit diesen Läufen auf höherer Ebene zu beginnen und dann die daraus gewonnenen Daten zu verwenden, um den Ausdruck des Tests mit dem niedrigsten gemeinsamen Nenner in Langform zu erstellen. Wenn Sie beispielsweise die Datenstruktur aus der
input
oben erstellten sichern, können Sie auf einfache Weise Folgendes erstellen :Art von Test, der auf der niedrigsten Ebene testet. Auf diese Weise erhalten Sie eine schöne Mischung: Eine Handvoll der grundlegendsten, primitivsten Tests (reine Komponententests), aber Sie haben noch keine Woche damit verbracht, Tests auf dieser primitiven Ebene zu schreiben. Das gibt Ihnen die Zeitressource, die Sie benötigen, um viel mehr, etwas weniger Atomtests mit dem
Parser
als Helfer zu schreiben . Endergebnis: Mehr Tests, mehr Abdeckung, mehr Eckdaten und andere interessante Fälle, besserer Code und höhere Qualitätssicherung.quelle
Natürlich ist es in Ordnung!
Sie benötigen immer einen Funktions- / Integrationstest, der den gesamten Codepfad abdeckt. Ein vollständiger Codepfad bedeutet in diesem Fall, dass der generierte Code ausgewertet wird. Das ist, dass Sie diese Analyse testen
x = 2 + 3 * a
- Code erzeugt, wenn Lauf mita = 5
Willen Satzx
zu17
und wenn Laufe mita = -2
gesetztx
zu-4
.Darunter sollten Sie Komponententests für kleinere Bits durchführen , solange dies tatsächlich zum Debuggen des Codes beiträgt . Je detaillierter die Tests sind, desto höher ist die Wahrscheinlichkeit, dass auch Änderungen am Code vorgenommen werden müssen, da sich die interne Schnittstelle ändert. Solche Tests haben wenig langfristigen Wert und führen zu zusätzlichen Wartungsarbeiten. Es gibt also einen Punkt, an dem die Renditen sinken, und Sie sollten davor aufhören.
quelle
Unit-Tests ermöglichen es Ihnen, bestimmte Elemente zu lokalisieren, die brechen und wo im Code sie brachen. Sie eignen sich also für sehr feinkörnige Tests. Gute Unit-Tests verkürzen die Debugging-Zeit.
Meiner Erfahrung nach sind Unit-Tests jedoch selten gut genug, um den korrekten Betrieb zu überprüfen. Integrationstests sind daher auch hilfreich, um eine Kette oder Abfolge von Vorgängen zu überprüfen. Integrationstests unterstützen Sie bei der Durchführung von Funktionstests. Wie Sie jedoch betont haben, ist es aufgrund der Komplexität der Integrationstests schwieriger, die spezifische Stelle im Code zu finden, an der der Test unterbrochen wird. Es ist auch etwas spröder, da Fehler an einer beliebigen Stelle in der Kette dazu führen, dass der Test fehlschlägt. Sie werden diese Kette jedoch weiterhin im Produktionscode haben, so dass das Testen der tatsächlichen Kette weiterhin hilfreich ist.
Idealerweise hätten Sie beides, aber in jedem Fall ist ein automatisierter Test im Allgemeinen besser als kein Test.
quelle
Führen Sie viele Tests mit dem Parser durch. Wenn der Parser die Tests besteht, speichern Sie diese Ausgaben in einer Datei, um den Parser zu verspotten und die andere Komponente zu testen.
quelle