Testen: deterministisch oder nicht deterministisch?

17

Ist es besser, eines von beiden zu haben?

  • Deterministische Testsuite, die zum Erfolg derselben Tests führt
  • Nicht deterministische Testsuite, die möglicherweise mehr Fälle abdeckt

?

Beispiel: Sie schreiben eine Testsuite, um die Controller-Funktionalität in einer MVC-Anwendung zu testen. Der Controller benötigt während des Tests Anwendungsdaten aus einer Datenbank als Eingabe. Hierfür gibt es zwei Möglichkeiten:

  • Sie codieren fest, welche Zeile (n) aus der Testdatenbank als Eingabe ausgewählt sind (z. B. die 10. und die 412. Zeile).
  • Sie verwenden einen Zufallszahlengenerator, um die Daten pseudozufällig aus der Datenbank auszuwählen (zwei Zeilen, die von einem Zufallszahlengenerator ausgewählt wurden).

Der erste ist deterministisch: Jeder Testlauf für dieselbe Coderevision sollte dasselbe Ergebnis liefern. Der zweite ist nicht deterministisch: Jeder Durchlauf der Testsuite hat die Möglichkeit, ein anderes Ergebnis zu erzielen. Die zufällig ausgewählten Daten könnten jedoch eine bessere Darstellung von Datenkantenfällen sein. Es könnte einen Benutzer simulieren, der unsere Steuerungen besser mit unvorhersehbaren Daten füttert.

Was sind Gründe, sich für einen anderen zu entscheiden?

DCKing
quelle
5
Dieser Test schlägt manchmal einfach fehl. martinfowler.com/articles/nonDeterminism.html
Danke für diesen Link. Angesichts dieses Artikels wollte ich klarstellen, dass Nichtdeterminismus im Kontext dieser Testsuite bedeutet. Da Daten zufällig aus einer Datenbank ausgewählt werden, sind alle Daten, die an den Controller gesendet werden, standardmäßig gültige Daten. Dies bedeutet, dass es in der Testsuite keine falsch-negativen Ergebnisse gibt, wenn es um Nichtdeterminismus geht. In gewisser Weise simuliert diese Zufälligkeit einen Benutzer, der Daten 'zufällig' zur Verwendung in einer Steuerung auswählt. Dies ist nicht unbedingt derselbe Nicht-Determinismus, den der Artikel diskutiert, oder?
DCKing
10
@DCKing: Überlegen Sie, was passiert, wenn Ihr Test fehlschlägt. Okay, du hast einen Fehler. Äh, was jetzt? Führen Sie es erneut im Debug-Modus aus! Wo es gelingt! Wie die nächsten hundert Male, wenn Sie es ausführen, und dann schreiben Sie das Problem als kosmischen Strahlenschlag ab. Nicht-Determinisim in Tests klingt absolut nicht praktikabel. Wenn Sie das Gefühl haben, dass Sie in Ihren Testfällen mehr Boden abdecken müssen, decken Sie mehr Boden ab. Initialisieren Sie Ihren RNG mit einem festgelegten Startwert und führen Sie den "Test" einige hundert Mal mit durchgehend zufälligen Werten aus.
Phoshi
1
(Endlich kam ich zu einer Maschine, auf der ich richtig nach Twitter suchen konnte - der " Dieser Test schlägt nur manchmal fehl " stammt von #FiveWordTechHorrors auf Twitter - wollte es richtig

Antworten:

30

Wenn Sie bei jedem Durchlauf der Testsuite die Möglichkeit haben, ein anderes Ergebnis zu erzielen, ist der Test nahezu wertlos. Wenn die Suite einen Fehler anzeigt, besteht eine hohe Wahrscheinlichkeit, dass Sie ihn nicht reproduzieren können, und wenn Sie versuchen, den Fehler zu beheben Fehler, Sie können nicht überprüfen, ob Ihr Fix funktioniert.

Wenn Sie also glauben, dass Sie eine Art Zufallszahlengenerator zum Generieren Ihrer Testdaten verwenden müssen, müssen Sie entweder sicherstellen, dass Sie den Generator immer mit demselben Startwert initialisieren, oder Ihre zufälligen Testdaten in einer Datei beibehalten, bevor Sie sie in Ihren Test einfügen. So können Sie den Test mit genau denselben Daten wie zuvor erneut ausführen. Auf diese Weise können Sie jeden nicht deterministischen Test in einen deterministischen Test umwandeln.

BEARBEITEN: Die Verwendung eines Zufallszahlengenerators zum Auswählen einiger Testdaten ist meiner Meinung nach manchmal ein Zeichen dafür, dass man beim Auswählen guter Testdaten zu faul ist . Anstatt 100.000 zufällig ausgewählte Testwerte zu werfen und zu hoffen, dass dies ausreicht, um alle schwerwiegenden Fehler zufällig zu entdecken, sollten Sie Ihr Gehirn besser nutzen, 10 bis 20 "interessante" Fälle auswählen und sie für die Testsuite verwenden. Dies führt nicht nur zu einer besseren Qualität Ihrer Tests, sondern auch zu einer viel höheren Leistung der Suite.

Doc Brown
quelle
Danke für deine Antwort. Wie ist Ihre Meinung zu dem Kommentar, den ich zu meiner Frage abgegeben habe?
DCKing
1
@DCKing: Wenn Sie der Meinung sind, dass ein Zufallsgenerator gute Testfälle besser auswählt als Sie (was ich bezweifle), verwenden Sie ihn einmal, um Kombinationen von Testdaten zu finden, bei denen Ihr Programm fehlschlägt, und fügen Sie diese Kombinationen in den "fest codierten" Teil ein Ihrer Testsuite.
Doc Brown
Danke noch einmal. Meine Antwort wurde aktualisiert, sodass sie nicht nur für MVC-Apps gilt.
DCKing
1
In einigen UI-Kontexten (z. B. bei Spielen, die Controller-Eingaben vornehmen) können Testprogramme, die zufällige Tasteneingaben generieren, für Stresstests nützlich sein. Sie können mit gezielten Eingaben schwer zu findende Fehler aufdecken.
Gort the Robot
@StevenBurnap: Nun, so wie ich die Frage verstehe, hatte das OP meiner Meinung nach konventionellere Regressionstests im Sinn. Natürlich stimme ich zu, dass Stresstests ein Sonderfall sind, der auch hardwareabhängig sein kann und zu nicht deterministischem Verhalten führt, selbst wenn Sie keinen Zufallsgenerator verwenden. Das ist etwas, was in dem Artikel beschrieben wird, auf den MichaelT im ersten Kommentar unter der Frage verweist. Und selbst bei Stresstests mit zufälliger Eingabe kann man zumindest versuchen, das Verhalten durch Verwendung eines definierten zufälligen Startwerts deterministischer zu gestalten.
Doc Brown
4

Sowohl deterministisch als auch nicht deterministisch haben einen Platz

Ich würde sie wie folgt aufteilen:

Unit-Tests.

Diese sollten deterministische, wiederholbare Tests mit genau den gleichen Daten jedes Mal haben. Unit-Tests begleiten bestimmte, isolierte Codeabschnitte und sollten sie deterministisch testen.

Funktions- und Eingangsstresstests.

Diese können den nicht deterministischen Ansatz mit den folgenden Einschränkungen verwenden:

  • diese Tatsache ist klar umrissen und hervorgehoben
  • Die ausgewählten Zufallswerte werden protokolliert und können manuell wiederholt werden
Michael Durrant
quelle
3

Beide.

Deterministische und nicht deterministische Tests haben unterschiedliche Anwendungsfälle und unterschiedliche Werte für Ihre Suite. Im Allgemeinen kann nicht deterministisch nicht die gleiche Präzision wie deterministisches Testen liefern, das langsam zu "nicht deterministischem Testen liefert keinen Wert" herangewachsen ist. Das ist falsch. Sie können weniger genau sein, aber sie können auch viel breiter sein, was seine eigenen Vorteile hat.

Nehmen wir ein Beispiel: Sie schreiben eine Funktion, die eine Liste von ganzen Zahlen sortiert. Was wären einige der deterministischen Komponententests, die Sie nützlich finden würden?

  • Eine leere Liste
  • Eine Liste mit nur einem Element
  • Eine Liste mit allen gleichen Elementen
  • Eine Liste mit mehreren eindeutigen Elementen
  • Eine Liste mit mehreren Elementen, von denen einige Duplikate sind
  • Eine Liste mit NaN, INT_MINundINT_MAX
  • Eine Liste, die bereits teilweise sortiert ist
  • Eine Liste mit 10.000.000 Elementen

Und das ist nur eine Sortierfunktion! Sicher, Sie könnten argumentieren, dass einige davon unnötig sind oder dass einige davon mit informeller Begründung ausgeschlossen werden können. Aber wir sind Ingenieure und wir haben informelle Argumente in die Luft jagen sehen. Wir wissen, dass wir nicht schlau genug sind, um die Systeme, die wir gebaut haben, vollständig zu verstehen oder die Komplexität in unseren Köpfen zu behalten. Deshalb schreiben wir in erster Linie Tests. Das Hinzufügen nicht deterministischer Tests besagt lediglich, dass wir möglicherweise nicht klug genug sind, um alle guten Tests a priori zu kennen. Indem Sie semi-zufällige Daten in Ihre Funktion einfließen lassen, ist die Wahrscheinlichkeit sehr viel höher, dass Sie einen Randfall finden, den Sie verpasst haben.

Das schließt natürlich auch deterministische Tests nicht aus. Nicht deterministische Tests helfen dabei, Fehler in großen Programmbereichen zu finden. Sobald Sie die Fehler gefunden haben, müssen Sie nachvollziehbar nachweisen, dass Sie sie behoben haben. So:

  • Verwenden Sie nicht deterministische Tests, um Fehler in Ihrem Code zu finden.
  • Verwenden Sie deterministische Tests, um Korrekturen in Ihrem Code zu überprüfen.

Beachten Sie, dass dies bedeutet, dass viele fundierte Ratschläge zu Komponententests nicht unbedingt für nicht deterministische Tests gelten. Zum Beispiel, dass sie schnell sein müssen. Low-Level-Eigenschaftstests sollten schnell sein, aber ein nicht deterministischer Test wie "simulieren Sie, dass ein Benutzer zufällig auf Schaltflächen auf Ihrer Website klickt, und stellen Sie sicher, dass Sie niemals einen 500-Fehler erhalten" sollte die Vollständigkeit der Geschwindigkeit vorziehen. Lassen Sie einfach einen solchen Test unabhängig von Ihrem Erstellungsprozess ausführen, damit die Entwicklung nicht verlangsamt wird. Führen Sie es beispielsweise auf einer eigenen privaten Staging-Box aus.

Hovercouch
quelle
-1

Sie wollen nicht deterministisch oder nicht deterministisch.

Was Sie vielleicht wollen, ist "immer gleich" vs. "nicht immer gleich".

Zum Beispiel könnten Sie eine Build-Nummer haben, die mit jedem Build zunimmt, und wenn Sie einige Zufallszahlen wollen, initialisieren Sie einen Zufallszahlengenerator mit der Build-Nummer als Startwert. Sie führen also bei jedem Build Ihre Tests mit unterschiedlichen Werten durch, sodass Sie mehr Chancen haben, Fehler zu finden.

Sobald jedoch ein Fehler gefunden wurde, müssen Sie den Test nur mit derselben Build-Nummer ausführen, und er ist reproduzierbar.

gnasher729
quelle
1
Wenn Sie keine Build-Nummer verwenden können, platzieren Sie den Anfangswert des Startwerts in der Ausgabe des Testlaufs, damit Sie erneut Tests mit demselben Startwert ausführen können.
RemcoGerlich