Ich wurde gefragt, wie man eine Suite mit 65.000.000.000 Tests durchführt, und ich frage mich, ob es normal ist, ein Projekt mit einer so großen Anzahl von Tests zu haben.
Haben Sie in Projekten mit diesem Merkmal gearbeitet?
Ich wurde gefragt, wie man eine Suite mit 65.000.000.000 Tests durchführt, und ich frage mich, ob es normal ist, ein Projekt mit einer so großen Anzahl von Tests zu haben.
Haben Sie in Projekten mit diesem Merkmal gearbeitet?
Antworten:
Bei 65 Milliarden Tests werden Sie aufgefordert, alle möglichen Eingaben zu testen. Dies ist nicht sinnvoll. Sie würden im Wesentlichen testen, ob Ihr Prozessor ordnungsgemäß funktioniert, und nicht, ob Ihr Code korrekt ist.
Sie sollten stattdessen Äquivalenzklassen testen . Dadurch wird der Bereich der Testeingaben drastisch reduziert.
Überlegen Sie auch, ob Sie Ihr System in kleinere Teile unterteilen können. Jedes Teil ist einfacher für sich zu testen, und dann können Sie einige Integrationstests durchführen, bei denen alle Teile zusammengeführt werden.
Wenn Sie immer noch die Gewissheit haben möchten, dass einige dieser Eingabekombinationen funktionieren, können Sie Fuzz-Tests ausprobieren . Sie erhalten einige der Vorteile des Testens vieler verschiedener Eingaben, ohne jedoch alle 65 Milliarden auszuführen.
Wenn dies eine echte Testsuite ist, möchten Sie nicht annähernd daran arbeiten.
Die ganze Aufgabe eines Testers ist es, ein Gleichgewicht zwischen gründlichen Tests zu finden, um sicherzugehen, dass Sie die "richtigen" Ergebnisse erhalten, und nur wenige Tests zu schreiben, die in angemessener Zeit ausgeführt werden können.
Viele Tests können in "Äquivalenzklassen" zusammengefasst werden. Dies bedeutet, dass Sie nicht 3 Milliarden Tests ausführen, sondern 1, was Ihnen ein angemessenes Maß an Sicherheit gibt, dass alle anderen Tests in dieser Äquivalenzklasse erfolgreich ausgeführt würden, wenn Sie sich entschließen, die zu verschwenden Zeit, sie laufen zu lassen.
Sie sollten jedem, der 65 Milliarden Tests durchführen möchte, mitteilen, dass er eine bessere Arbeit leisten muss, um Tests in Äquivalenzklassen zusammenzufassen.
Höchstwahrscheinlich sind Sie zu Ihrer Zahl von 65 Milliarden Tests gelangt, indem Sie alle möglichen Kombinationen von Eingaben in das zu testende System berechnet haben oder indem Sie die zyklomatische Komplexität berechnet haben und davon ausgegangen sind, dass für jeden dieser eindeutigen Ausführungspfade ein Test geschrieben werden muss.
So werden echte Tests nicht geschrieben, denn wie andere Poster und Kommentatoren angedeutet haben, ist die technische Leistung, die erforderlich ist, um 65 Milliarden auszuführenTests ist atemberaubend. Dies wäre wie das Schreiben eines Tests, bei dem eine Methode zum Hinzufügen von zwei Ganzzahlen ausgeführt wird, indem jede mögliche Permutation von zwei 32-Bit-Werten eingegeben und das Ergebnis überprüft wird. Es ist völliger Wahnsinn. Sie müssen die Grenze ziehen und eine Teilmenge aller möglichen Testfälle identifizieren, um sicherzustellen, dass sich das System im gesamten Eingabebereich erwartungsgemäß verhält. Zum Beispiel. Sie testen das Hinzufügen einiger "gewöhnlicher" Zahlen, Sie testen einige Szenarien mit negativen Zahlen, Sie testen technische Grenzen wie Überlaufszenarien und Sie testen alle Szenarien, die zu Fehlern führen sollten. Wie bereits erwähnt, üben diese verschiedenen Arten von Tests "Äquivalenzklassen" aus. Sie ermöglichen es Ihnen, eine repräsentative Stichprobe der möglichen Eingaben zusammen mit allen bekannten "Ausreißern" zu entnehmen.
Betrachten Sie einen der grundlegenden Code-Katas, den Roman Numeral Generator. Die Aufgabe, die mit TDD-Techniken im Dojo-Stil ausgeführt werden muss, besteht darin, eine Funktion zu schreiben, die eine beliebige Zahl von 1 bis 3000 akzeptiert und die richtige römische Zahl für diesen Zahlenwert erzeugt.
Sie können dieses Problem nicht lösen, indem Sie 3000 Komponententests einzeln schreiben und diese der Reihe nach bestehen. Das ist Wahnsinn; Die Übung dauert normalerweise zwischen einer und zwei Stunden, und Sie sind tagelang dort, um jeden einzelnen Wert zu testen. Stattdessen wirst du schlau. Sie beginnen mit dem einfachsten Basisfall (1 == "I"), implementieren diesen mit einer "Least-Code" -Strategie ( return "I";
) und prüfen dann, wie sich der Code in einem anderen erwarteten Szenario falsch verhält (2 == " II "). Spülen und wiederholen; Höchstwahrscheinlich haben Sie Ihre ursprüngliche Implementierung durch etwas ersetzt, das das "I" -Zeichen so oft wie nötig (wie return new String('I',number);
) wiederholt . Das wird offensichtlich einen Test für III bestehen, also stört es dich nicht; Stattdessen schreiben Sie den Test für 4 == "IV", von dem Sie wissen, dass die aktuelle Implementierung gewonnen hat. '
Oder Sie untersuchen in einem analytischeren Stil jede bedingte Entscheidung, die vom Code getroffen wird (oder getroffen werden muss), und schreiben einen Test, der den Code für jedes mögliche Ergebnis jeder Entscheidung eingibt. Wenn Sie 5 if-Anweisungen haben (jede hat eine wahre und eine falsche Verzweigung), von denen jede völlig unabhängig von der anderen ist, codieren Sie 10 Tests, nicht 32. Jeder Test soll zwei Dinge über eine bestimmte mögliche Entscheidung aussagen; Zuerst, dass die richtige Entscheidung getroffen wurde und dann, dass der unter dieser Bedingung eingegebene Code korrekt ist. Sie nicht einen Test für jede mögliche Permutation von unabhängigen Entscheidungen codieren. Wenn die Entscheidungen abhängig sind, müssen Sie mehr von ihnen in Kombination testen, aber es gibt weniger solche Kombinationen, da einige Entscheidungen nur dann getroffen werden, wenn eine andere Entscheidung ein bestimmtes Ergebnis hatte.
Ist das "normal" ?, nein. Wobei "normal" als durchschnittliche oder typische Erfahrung definiert ist. Ich kann nicht sagen, dass ich jemals an einem solchen Projekt arbeiten musste, aber ich war an einem Projekt, bei dem einer von ein paar Millionen Bits umgedreht wurde. Das zu testen war ... eine Herausforderung.
Ist es möglicherweise erforderlich? Nun, das hängt von den Garantien und Besonderheiten des Projekts ab. Es ist ein bisschen ungläubig, es zuerst zu verstehen, aber Ihre Frage ist leicht auf Besonderheiten.
Wie andere (MichaelT) bereits betont haben, macht die Zeit, um diese Aufgabe mit seriellen Tests abzuschließen, dies unpraktisch. So wird die Parallelisierung zu Ihrer ersten Überlegung. Wie viele Testsysteme können Sie bei diesem Problem einsetzen, und welche Unterstützung haben Sie für die Zusammenstellung der Ergebnisse dieser mehreren Systeme?
Welche Garantien haben Sie dafür, dass das Gerät oder der Algorithmus, den Sie testen, zuverlässig repliziert wird? Software ist bei der Replikation ziemlich zuverlässig, aber bei Hardwaregeräten (insbesondere der ersten Generation) können Herstellungsprobleme auftreten. Ein falscher Testfehler in diesem Fall kann entweder auf einen schlechten Algorithmus hindeuten oder darauf, dass das Gerät nicht richtig zusammengebaut wurde. Müssen Sie zwischen diesen beiden Fällen unterscheiden?
Sie müssen auch überlegen, wie Sie die Testsysteme selbst validieren. Vorausgesetzt, Sie haben einen legitimen Grund für diese vielen Testfälle und benötigen viel Automatisierung. Diese Automatisierung muss überprüft werden, um sicherzustellen, dass beim Generieren Ihrer Testfälle keine Fehler auftreten. Stichprobenkontrollen auf Fehler wären wirklich gleichbedeutend mit dem Auffinden einer Nadel im Heuhaufen.
Dieser arstechnica-Link gibt möglicherweise einen Einblick in Ihre Testüberlegungen . GPU-Cluster werden häufig zum Knacken von Kennwörtern mit Brute-Force-Methoden verwendet. Der in dem Artikel zitierte kann can cycle through as many as 350 billion guesses per second
, so dass diese Art von Ihre 65B-Tests in Perspektive setzt. Es ist wahrscheinlich eine andere Domäne, aber es zeigt, wie die Annäherung an die Aufgabe aus verschiedenen Blickwinkeln zu einer realisierbaren Lösung führen kann.
Ich denke nicht, dass es machbar ist, 6.5e + 10-Tests an erster Stelle beizubehalten , so dass das Ausführen möglicherweise umstritten ist. Selbst die größten Projekte, wie Debian mit all seinen Paketen, haben insgesamt nur einige hundert Millionen SLOCs.
Aber wenn Sie trotzdem eine große Anzahl von Tests durchführen müssen, gibt es ein paar Strategien.
Lass sie nicht alle laufen. Höchstwahrscheinlich hängt nicht jeder Test von jedem Codepfad ab. Wenn Sie Abhängigkeiten zwischen Subsystemen und deren Tests sowie zwischen Testsuiten definieren, können Sie nur Komponententests ausführen, die für eine bestimmte Änderung relevant sind, und nur die Integrationstests, die von diesen Komponententests abhängen.
Führen Sie sie parallel aus. Wenn die Codebasis so groß ist, haben Sie wahrscheinlich eine massive Buildfarm (damals, bei JetBrains, einem relativ kleinen Vorgang, liefen 40-50 Build-Agenten allein auf der IDEA Continuous Build / Integration-Farm). Da Komponententests unabhängig sind und Integrationstests bereits erstellten Code wiederverwenden können, lassen sich Tests relativ einfach parallelisieren.
Hör früh auf zu rennen. Wenn Sie wissen, dass eine bestimmte Testsuite für ihre angemessene Funktionsweise von der Richtigkeit einer anderen Testsuite abhängt, können Sie die gesamte Kette durchtrennen, sobald ein Link ausfällt.
Haftungsausschluss: Ich bin kein professioneller Prüfingenieur. Nimm das Obige mit einem Körnchen Salz.
Obwohl es hier einige gute Vorschläge gibt, wie Sie versuchen können, mit weniger Tests vorbeizukommen, bezweifle ich ernsthaft, dass Ihr System nur 65 Milliarden Eingangskombinationen hat. Das sind weniger als 36 Bit Eingabe. Nehmen wir an, Sie haben bereits alle oben genannten Ratschläge befolgt.
Wenn die Ausführung jedes Tests etwa eine Millisekunde dauert und Sie die Tests auf nur 10 Prozessoren (einen normalen PC) verteilen, dauert der Test etwas mehr als 69 Tage. Das ist eine Weile, aber nicht völlig unvernünftig. Verteilen Sie es auf 100 Prozessoren (ein Dutzend normale PCs oder einen vernünftigen Server-PC), und die Tests werden in weniger als 7 Tagen abgeschlossen sein. Sie können diese jede Woche ausführen, um nach Regressionen zu suchen.