Ich habe einen Kollegen, der Komponententests für Objekte schreibt, die ihre Felder mit zufälligen Daten füllen. Sein Grund ist, dass es einen größeren Testbereich bietet, da es viele verschiedene Werte testet, während ein normaler Test nur einen einzigen statischen Wert verwendet.
Ich habe ihm verschiedene Gründe dafür genannt, die wichtigsten sind:
- Zufällige Werte bedeuten, dass der Test nicht wirklich wiederholbar ist (was auch bedeutet, dass der Test, wenn er zufällig fehlschlagen kann, dies auf dem Build-Server tun und den Build abbrechen kann).
- Wenn es sich um einen zufälligen Wert handelt und der Test fehlschlägt, müssen wir a) das Objekt reparieren und b) uns zwingen, jedes Mal auf diesen Wert zu testen, damit wir wissen, dass er funktioniert, aber da er zufällig ist, wissen wir nicht, was der Wert war
Ein anderer Mitarbeiter fügte hinzu:
- Wenn ich eine Ausnahme teste, stellen zufällige Werte nicht sicher, dass der Test im erwarteten Zustand endet
- Zufallsdaten werden zum Ausspülen eines Systems und zum Testen der Last verwendet, nicht für Komponententests
Kann jemand andere zusätzliche Gründe hinzufügen, die ich ihm geben kann, damit er damit aufhört?
(Oder ist dies alternativ eine akzeptable Methode zum Schreiben von Komponententests, und ich und mein anderer Mitarbeiter liegen falsch?)
unit-testing
tdd
mocking
Adam V.
quelle
quelle
Antworten:
Es gibt einen Kompromiss. Ihr Mitarbeiter ist eigentlich auf etwas, aber ich denke, er macht es falsch. Ich bin mir nicht sicher, ob völlig zufällige Tests sehr nützlich sind, aber es ist sicherlich nicht ungültig.
Eine Programm- (oder Einheiten-) Spezifikation ist eine Hypothese, dass es ein Programm gibt, das diese erfüllt. Das Programm selbst ist dann ein Beweis für diese Hypothese. Was Unit-Tests sein sollten, ist ein Versuch, Gegenbeweise zu liefern, um zu widerlegen, dass das Programm gemäß der Spezifikation funktioniert.
Jetzt können Sie die Komponententests von Hand schreiben, aber es ist wirklich eine mechanische Aufgabe. Es kann automatisiert werden. Alles, was Sie tun müssen, ist die Spezifikation zu schreiben, und eine Maschine kann viele, viele Komponententests generieren, die versuchen, Ihren Code zu brechen.
Ich weiß nicht, welche Sprache Sie verwenden, aber siehe hier:
Java http://functionaljava.org/
Scala (oder Java) http://github.com/rickynils/scalacheck
Haskell http://www.cs.chalmers.se/~rjmh/QuickCheck/
.NET: http://blogs.msdn.com/dsyme/archive/2008/08/09/fscheck-0-2.aspx
Diese Tools verwenden Ihre wohlgeformte Spezifikation als Eingabe und generieren automatisch so viele Komponententests wie Sie möchten, mit automatisch generierten Daten. Sie verwenden "Schrumpf" -Strategien (die Sie optimieren können), um den einfachsten Testfall zu finden, um Ihren Code zu brechen und um sicherzustellen, dass er die Randfälle gut abdeckt.
Viel Spaß beim Testen!
quelle
Diese Art des Testens wird als Affentest bezeichnet . Wenn es richtig gemacht wird, kann es Insekten aus den wirklich dunklen Ecken rauchen.
Um Ihre Bedenken hinsichtlich der Reproduzierbarkeit auszuräumen: Der richtige Weg, dies zu erreichen, besteht darin, die fehlgeschlagenen Testeinträge aufzuzeichnen und einen Komponententest zu generieren, der die gesamte Familie des spezifischen Fehlers untersucht. und in den Komponententest die eine spezifische Eingabe (aus den Zufallsdaten) einbeziehen, die den anfänglichen Fehler verursacht hat.
quelle
Hier gibt es ein Haus auf halbem Weg, das eine Verwendung hat, nämlich Ihr PRNG mit einer Konstanten zu besäen. Auf diese Weise können Sie zufällige Daten generieren, die wiederholbar sind.
Persönlich denke ich, dass es Orte gibt, an denen (konstante) Zufallsdaten beim Testen nützlich sind - nachdem Sie glauben, alle sorgfältig durchdachten Ecken erledigt zu haben, kann die Verwendung von Stimuli aus einem PRNG manchmal andere Dinge finden.
quelle
In dem Buch Beautiful Code gibt es ein Kapitel namens "Beautiful Tests", in dem er eine Teststrategie für den binären Suchalgorithmus durchläuft . Ein Absatz heißt "Random Acts of Testing", in dem er zufällige Arrays erstellt, um den Algorithmus gründlich zu testen. Sie können einige davon online in Google Books, Seite 95, lesen , , aber es ist ein großartiges Buch, das sich lohnt.
Im Grunde zeigt dies nur, dass das Generieren von Zufallsdaten zum Testen eine praktikable Option ist.
quelle
Ich bin für zufällige Tests und schreibe sie. Ob sie in einer bestimmten Build-Umgebung geeignet sind und in welchen Testsuiten sie enthalten sein sollten, ist jedoch eine differenziertere Frage.
Lokale Tests (z. B. über Nacht auf Ihrer Entwicklungsbox) haben ergeben, dass zufällige und offensichtliche Fehler vorliegen. Die obskuren sind arkan genug, dass ich denke, dass zufällige Tests wirklich die einzig realistische waren, um sie auszuspülen. Als Test nahm ich einen schwer zu findenden Fehler, der durch randomisierte Tests entdeckt wurde, und ließ ein halbes Dutzend Crack-Entwickler die Funktion (etwa ein Dutzend Codezeilen) überprüfen, in der sie auftrat. Keiner konnte es erkennen.
Viele Ihrer Argumente gegen randomisierte Daten sind Aromen von "Der Test ist nicht reproduzierbar". Ein gut geschriebener randomisierter Test erfasst jedoch den Samen, der zum Starten des randomisierten Samens verwendet wurde, und gibt ihn bei einem Fehler aus. Auf diese Weise können Sie den Test nicht nur manuell wiederholen, sondern auch trivial einen neuen Test erstellen, der das spezifische Problem testet, indem Sie den Startwert für diesen Test fest codieren. Natürlich ist es wahrscheinlich besser, einen expliziten Test, der diesen Fall abdeckt, von Hand zu codieren, aber Faulheit hat ihre Tugenden, und dies ermöglicht es Ihnen sogar, neue Testfälle im Wesentlichen automatisch aus einem fehlerhaften Startwert zu generieren.
Der einzige Punkt, den Sie ansprechen, über den ich jedoch nicht diskutieren kann, ist, dass er die Build-Systeme zerstört. Die meisten Build- und Continuous-Integration-Tests erwarten, dass die Tests jedes Mal dasselbe tun. Ein Test, der zufällig fehlschlägt, erzeugt Chaos, schlägt zufällig fehl und zeigt mit den Fingern auf harmlose Änderungen.
Eine Lösung besteht dann darin, Ihre randomisierten Tests weiterhin als Teil der Build- und CI-Tests auszuführen, sie jedoch mit einem festen Startwert für eine feste Anzahl von Iterationen auszuführen . Daher macht der Test immer das Gleiche, untersucht jedoch immer noch einen Teil des Eingabebereichs (wenn Sie ihn für mehrere Iterationen ausführen).
Vor Ort, z. B. wenn Sie die betreffende Klasse ändern, können Sie sie für weitere Iterationen oder mit anderen Seeds ausführen. Wenn randomisierte Tests jemals populärer werden, können Sie sich sogar eine bestimmte Reihe von Tests vorstellen, von denen bekannt ist, dass sie zufällig sind, die mit unterschiedlichen Samen durchgeführt werden können (daher mit zunehmender Abdeckung im Laufe der Zeit) und bei denen Fehler nicht dasselbe bedeuten würden als deterministische CI-Systeme (dh Läufe sind nicht 1: 1 mit Codeänderungen verbunden, sodass Sie nicht mit dem Finger auf eine bestimmte Änderung zeigen, wenn etwas fehlschlägt).
Für randomisierte Tests, insbesondere für gut geschriebene, gibt es viel zu sagen. Seien Sie also nicht zu schnell, um sie zu verwerfen!
quelle
Wenn Sie TDD machen, würde ich argumentieren, dass zufällige Daten ein ausgezeichneter Ansatz sind. Wenn Ihr Test mit Konstanten geschrieben ist, können Sie nur garantieren, dass Ihr Code für den bestimmten Wert funktioniert. Wenn Ihr Test auf dem Build-Server zufällig fehlschlägt, liegt wahrscheinlich ein Problem mit der Schreibweise des Tests vor.
Durch zufällige Daten wird sichergestellt, dass künftiges Refactoring nicht auf einer magischen Konstante beruht. Wenn Ihre Tests Ihre Dokumentation sind, bedeutet das Vorhandensein von Konstanten dann nicht, dass sie nur für diese Konstanten funktionieren müssen?
Ich übertreibe, aber ich ziehe es vor, zufällige Daten in meinen Test einzufügen, als Zeichen dafür, dass "der Wert dieser Variablen das Ergebnis dieses Tests nicht beeinflussen sollte".
Ich werde jedoch sagen, dass wenn Sie eine Zufallsvariable verwenden und dann Ihren Test basierend auf dieser Variablen abspalten, das ein Geruch ist.
quelle
Ein Vorteil für jemanden, der sich die Tests ansieht, ist, dass willkürliche Daten eindeutig nicht wichtig sind. Ich habe zu viele Tests mit Dutzenden von Daten gesehen, und es kann schwierig sein zu sagen, was so sein muss und was gerade so ist. Wenn beispielsweise eine Adressvalidierungsmethode mit einer bestimmten Postleitzahl getestet wird und alle anderen Daten zufällig sind, können Sie ziemlich sicher sein, dass die Postleitzahl der einzige wichtige Teil ist.
quelle
Wenn Ihr Testfall nicht genau aufzeichnet, was er testet, müssen Sie den Testfall möglicherweise neu codieren. Ich möchte immer Protokolle haben, auf die ich für Testfälle zurückgreifen kann, damit ich genau weiß, warum es fehlgeschlagen ist, ob statische oder zufällige Daten verwendet werden.
quelle
Ihr Mitarbeiter führt Fuzz-Tests durch , obwohl er nichts davon weiß. Sie sind besonders wertvoll in Serversystemen.
quelle
Können Sie einige zufällige Daten einmal generieren (ich meine genau einmal, nicht einmal pro Testlauf) und sie anschließend in allen Tests verwenden?
Ich kann definitiv erkennen, welchen Wert es hat, zufällige Daten zu erstellen, um Fälle zu testen, an die Sie nicht gedacht haben, aber Sie haben Recht, Unit-Tests, die zufällig bestanden oder nicht bestanden werden können, sind eine schlechte Sache.
quelle
Sie sollten sich fragen, was das Ziel Ihres Tests ist.
Bei Unit-Tests geht es darum, Logik, Codefluss und Objektinteraktionen zu überprüfen. Durch die Verwendung von Zufallswerten wird versucht, ein anderes Ziel zu erreichen, wodurch der Testfokus und die Einfachheit verringert werden. Es ist aus Gründen der Lesbarkeit akzeptabel (Generieren von UUID, IDs, Schlüsseln usw.)
Speziell für Unit-Tests kann ich mich nicht erinnern, auch wenn diese Methode erfolgreich Probleme gefunden hat. Aber ich habe viele Determinismusprobleme (in den Tests) gesehen, die versucht haben, mit zufälligen Werten und hauptsächlich mit zufälligen Daten klug zu sein.
Fuzz-Tests sind ein gültiger Ansatz für Integrationstests und End-to-End-Tests .
quelle
Wenn Sie für Ihre Tests zufällige Eingaben verwenden, müssen Sie die Eingaben protokollieren, damit Sie die Werte sehen können. Auf diese Weise können Sie den Test schreiben, um ihn zu reproduzieren , wenn Sie auf einen Randfall stoßen . Ich habe die gleichen Gründe von Leuten gehört, die keine zufälligen Eingaben verwenden, aber sobald Sie einen Einblick in die tatsächlichen Werte haben, die für einen bestimmten Testlauf verwendet wurden, ist dies kein so großes Problem.
Der Begriff "willkürliche" Daten ist auch sehr nützlich, um etwas zu kennzeichnen, das nicht wichtig ist. Wir haben einige Abnahmetests, die in den Sinn kommen, wenn es viele Rauschdaten gibt, die für den vorliegenden Test nicht relevant sind.
quelle
Abhängig von Ihrem Objekt / Ihrer App haben zufällige Daten einen Platz im Lasttest. Ich denke, wichtiger wäre es, Daten zu verwenden, die die Randbedingungen der Daten explizit testen.
quelle
Wir sind heute gerade darauf gestoßen. Ich wollte Pseudozufalls (also würde es in Bezug auf die Größe wie komprimierte Audiodaten aussehen). Ich wollte, dass ich auch deterministisch wollte . rand () war unter OSX anders als unter Linux. Und wenn ich nicht wieder gesät habe, kann sich das jederzeit ändern. Deshalb haben wir es so geändert, dass es deterministisch, aber immer noch pseudozufällig ist: Der Test ist wiederholbar, genauso wie die Verwendung von vordefinierten Daten (aber bequemer geschrieben).
Dies wurde NICHT durch zufällige Brute Force durch Codepfade getestet. Das ist der Unterschied: Immer noch deterministisch, immer noch wiederholbar, immer noch Daten verwendet, die wie echte Eingaben aussehen, um eine Reihe interessanter Überprüfungen von Kantenfällen in komplexer Logik durchzuführen. Noch Unit-Tests.
Ist das noch zufällig? Reden wir über Bier. :-)
quelle
Ich kann mir drei Lösungen für das Testdatenproblem vorstellen:
Ich würde empfehlen, all das zu tun . Das heißt, schreiben Sie wiederholbare Komponententests mit einigen Randfällen, die mit Ihrem Gehirn erarbeitet wurden, und einigen randomisierten Daten, die Sie nur einmal generieren. Eine Reihe von randomisierten Tests Dann schreiben Sie, dass Sie laufen auch .
Von den randomisierten Tests sollte niemals erwartet werden, dass sie etwas auffangen, was Ihre wiederholbaren Tests verpassen. Sie sollten versuchen, alles mit wiederholbaren Tests abzudecken, und die randomisierten Tests als Bonus betrachten. Wenn sie etwas finden, sollte es etwas sein, das Sie nicht vernünftigerweise vorhergesagt haben könnten; eine echte Kuriosität.
quelle
Wie kann Ihr Mann den Test erneut ausführen, wenn nicht festgestellt wurde, ob er ihn behoben hat? Dh er verliert die Wiederholbarkeit von Tests.
Während ich denke, dass es wahrscheinlich einen gewissen Wert hat, eine Menge zufälliger Daten bei Tests zu schleudern, fällt dies, wie in anderen Antworten erwähnt, mehr unter die Überschrift Lasttest als alles andere. Es ist so ziemlich eine "Prüfung durch Hoffnung" -Praxis. Ich denke, dass Ihr Typ in Wirklichkeit einfach nicht darüber nachdenkt, was er zu testen versucht, und diesen Mangel an Gedanken auszugleichen, indem er hofft, dass Zufälligkeit irgendwann einen mysteriösen Fehler einfängt.
Das Argument, das ich mit ihm verwenden würde, ist, dass er faul ist. Oder anders ausgedrückt: Wenn er sich nicht die Zeit nimmt, um zu verstehen, was er zu testen versucht, zeigt dies wahrscheinlich, dass er den Code, den er schreibt, nicht wirklich versteht.
quelle