Ich arbeite häufig mit sehr numerischen / mathematischen Programmen, bei denen es schwierig ist, das genaue Ergebnis einer Funktion vorherzusagen.
Bei dem Versuch, TDD mit dieser Art von Code anzuwenden, ist das Schreiben des zu testenden Codes häufig wesentlich einfacher als das Schreiben von Komponententests für diesen Code, da ich das erwartete Ergebnis nur mit der Anwendung des Algorithmus selbst (unabhängig davon, ob in meinem Kopf, auf Papier oder am Computer). Dies fühlt sich falsch an, da ich den getesteten Code effektiv verwende, um meine Komponententests zu verifizieren, anstatt umgekehrt.
Gibt es bekannte Techniken zum Schreiben von Komponententests und zum Anwenden von TDD, wenn das Ergebnis des zu testenden Codes schwer vorherzusagen ist?
Ein (reales) Beispiel für Code mit schwer vorhersagbaren Ergebnissen:
Eine Funktion weightedTasksOnTime
, die eine Menge an Arbeit pro Tag getan gegeben workPerDay
in Bereich (0, 24], die aktuelle Uhrzeit initialTime
> 0, und eine Liste von Aufgaben taskArray
, die jeweils mit einer Zeit abzuschließen Eigentum time
> 0, Fälligkeit due
und Wichtigkeitswert importance
; Renditen Ein normalisierter Wert im Bereich [0, 1], der die Wichtigkeit von Aufgaben darstellt, die vor ihrem due
Datum erledigt werden können, wenn jede Aufgabe in der von angegebenen Reihenfolge erledigt wird taskArray
, beginnend mit initialTime
.
Der Algorithmus zum Implementieren dieser Funktion ist relativ einfach: Iterieren Sie über Tasks in taskArray
. Für jede Aufgabe hinzufügen time
zu initialTime
. Wenn die neue Zeit <ist due
, fügen Sie importance
sie einem Akkumulator hinzu. Die Zeit wird durch inverse workPerDay angepasst. Teilen Sie vor der Rückgabe des Akkus die zu normalisierenden Aufgabenbedeutungen durch die Summe.
function weightedTasksOnTime(workPerDay, initialTime, taskArray) {
let simulatedTime = initialTime
let accumulator = 0;
for (task in taskArray) {
simulatedTime += task.time * (24 / workPerDay)
if (simulatedTime < task.due) {
accumulator += task.importance
}
}
return accumulator / totalImportance(taskArray)
}
Ich glaube, dass das obige Problem unter Beibehaltung seines Kerns vereinfacht werden kann, indem workPerDay
die Normierungsanforderung beseitigt wird, um Folgendes zu ergeben:
function weightedTasksOnTime(initialTime, taskArray) {
let simulatedTime = initialTime
let accumulator = 0;
for (task in taskArray) {
simulatedTime += task.time
if (simulatedTime < task.due) {
accumulator += task.importance
}
}
return accumulator
}
Diese Frage befasst sich mit Situationen, in denen der zu testende Code keine Neuimplementierung eines vorhandenen Algorithmus ist. Wenn es sich bei Code um eine Neuimplementierung handelt, lassen sich die Ergebnisse von sich aus leicht vorhersagen, da vorhandene vertrauenswürdige Implementierungen des Algorithmus als natürliches Testorakel fungieren.
quelle
Antworten:
Es gibt zwei Dinge, die Sie in schwer zu testendem Code testen können. Erstens die entarteten Fälle. Was passiert, wenn sich in Ihrem Task-Array keine oder nur eines oder zwei Elemente befinden, aber eines nach dem Fälligkeitsdatum liegt usw. Alles, was einfacher als Ihr eigentliches Problem ist, aber dennoch vernünftig ist, manuell zu berechnen.
Der zweite ist die Sanity Checks. Dies sind die Prüfungen, bei denen Sie nicht wissen, ob eine Antwort richtig ist , aber auf jeden Fall wissen, ob sie falsch ist . Dies sind Dinge wie Zeit muss voranschreiten, Werte müssen in einem vernünftigen Bereich liegen, Prozentsätze müssen sich zu 100 addieren usw.
Ja, dies ist nicht so gut wie ein vollständiger Test, aber Sie werden überrascht sein, wie oft Sie die Überprüfung der geistigen Gesundheit und entartete Fälle durcheinander bringen, was ein Problem in Ihrem vollständigen Algorithmus aufdeckt.
quelle
Früher habe ich Tests für wissenschaftliche Software mit schwer vorhersehbaren Ergebnissen geschrieben. Wir haben viel von metamorphen Beziehungen Gebrauch gemacht. Grundsätzlich wissen Sie, wie sich Ihre Software verhalten soll, auch wenn Sie keine genauen numerischen Ausgaben kennen.
Ein mögliches Beispiel für Ihren Fall: Wenn Sie die Menge der Arbeit, die Sie täglich erledigen können, verringern, bleibt die Gesamtmenge der Arbeit, die Sie erledigen können, bestenfalls gleich, nimmt jedoch wahrscheinlich ab. Führen Sie die Funktion für eine Reihe von Werten von aus
workPerDay
und stellen Sie sicher, dass die Beziehung gültig ist.quelle
Die anderen Antworten haben gute Ideen für die Entwicklung von Tests für Kanten- oder Fehlerfälle. Für die anderen ist die Verwendung des Algorithmus selbst nicht ideal (offensichtlich), aber dennoch nützlich.
Es erkennt, ob sich der Algorithmus (oder die Daten, von denen er abhängt) geändert hat
Wenn die Änderung ein Unfall ist, können Sie ein Commit zurücksetzen. Wenn die Änderung beabsichtigt war, müssen Sie den Komponententest erneut durchführen.
quelle
So schreiben Sie Unit-Tests für jede andere Art von Code:
Sofern Ihr Code kein zufälliges Element enthält oder nicht deterministisch ist (dh nicht dieselbe Ausgabe bei derselben Eingabe erzeugt), ist er einheitentestbar.
Vermeiden Sie Nebenwirkungen oder Funktionen, die von äußeren Kräften beeinflusst werden. Reine Funktionen sind einfacher zu testen.
quelle
Unless your code involves some random element
Der Trick dabei ist, Ihren Zufallszahlengenerator zu einer injizierten Abhängigkeit zu machen, sodass Sie ihn dann durch einen Zahlengenerator ersetzen können, der genau das Ergebnis liefert, das Sie möchten. Auf diese Weise können Sie erneut genau testen und die generierten Zahlen auch als Eingabeparameter zählen.not deterministic (i.e. it won't produce the same output given the same input)
Da ein Komponententest von einer kontrollierten Situation ausgehen sollte, kann er nur dann nicht deterministisch sein, wenn er ein zufälliges Element enthält, das Sie dann injizieren können. Ich kann mir hier keine anderen Möglichkeiten vorstellen.if(x == x)
, als wäre es ein sinnloser Vergleich. Sie müssen Ihre beiden Ergebnisse ( tatsächlich : stammt aus dem Code; erwartet : stammt aus Ihrem externen Wissen) voneinander unabhängig sein.Update aufgrund von geposteten Kommentaren
Die ursprüngliche Antwort wurde aus Gründen der Kürze entfernt. Sie finden sie im Bearbeitungsverlauf.
Erstens ein TL; DR , um eine sonst lange Antwort zu vermeiden:
Das Hauptproblem hierbei ist, dass Sie keine Trennung zwischen dem Kunden und dem Entwickler (und dem Analysten - obwohl diese Rolle auch von einem Entwickler vertreten werden kann) vornehmen.
Sie müssen zwischen dem Testen des Codes und dem Testen der Geschäftsanforderungen unterscheiden.
Zum Beispiel möchte der Kunde es so arbeiten [dieser] . Allerdings verkennt der Entwickler, und er schreibt Code, der tut , [die] .
Der Entwickler schreibt daher Komponententests, die prüfen, ob [das] wie erwartet funktioniert. Wenn er die Anwendung richtig entwickelt, wird seine Einheit Tests bestehen , auch wenn die Anwendung nicht tut , [diese] , die der Kunde es erwartet hatte.
Wenn Sie die Erwartungen des Kunden (die Geschäftsanforderungen) testen möchten, muss dies in einem separaten (und späteren) Schritt erfolgen.
Ein einfacher Entwicklungsworkflow, der Ihnen zeigt, wann diese Tests ausgeführt werden sollten:
Sie fragen sich vielleicht, warum es wichtig ist, zwei separate Tests durchzuführen, wenn Kunde und Entwickler ein und dasselbe sind. Da es keine "Übergabe" vom Entwickler an den Kunden gibt, werden die Tests nacheinander ausgeführt, es handelt sich jedoch immer noch um separate Schritte.
Wenn Sie testen möchten, ob Ihr Algorithmus selbst korrekt ist , gehört dies nicht zur Aufgabe des Entwicklers . Dies ist Sache des Kunden, und der Kunde wird dies anhand der Anwendung testen .
Als Unternehmer und Akademiker fehlt Ihnen hier möglicherweise eine wichtige Unterscheidung, die die unterschiedlichen Verantwortlichkeiten verdeutlicht.
quelle
Eigenschaftsprüfung
Manchmal werden mathematische Funktionen durch "Eigenschaftstests" besser erfüllt als durch herkömmliche beispielbasierte Komponententests. Stellen Sie sich zum Beispiel vor, Sie schreiben Unit-Tests für eine ganzzahlige "Multiplikations" -Funktion. Obwohl die Funktion selbst sehr einfach zu sein scheint, wie können Sie sie gründlich testen, wenn dies die einzige Möglichkeit zur Multiplikation ist, ohne dass die Logik in der Funktion selbst vorhanden ist? Sie können Riesen-Tabellen mit erwarteten Ein- / Ausgängen verwenden, dies ist jedoch begrenzt und fehleranfällig.
In diesen Fällen können Sie bekannte Eigenschaften der Funktion testen, anstatt nach bestimmten erwarteten Ergebnissen zu suchen. Bei der Multiplikation wissen Sie möglicherweise, dass das Multiplizieren einer negativen und einer positiven Zahl zu einer negativen Zahl und das Multiplizieren von zwei negativen Zahlen zu einer positiven Zahl usw. führen sollte. Verwenden Sie zufällige Werte und überprüfen Sie, ob diese Eigenschaften für alle erhalten bleiben Testwerte sind eine gute Möglichkeit, solche Funktionen zu testen. Sie müssen im Allgemeinen auf mehr als eine Eigenschaft testen, können jedoch häufig eine endliche Menge von Eigenschaften identifizieren, die zusammen das korrekte Verhalten einer Funktion validieren, ohne das erwartete Ergebnis für jeden Fall zu kennen.
Eine der besten Einführungen in Property Testing, die ich gesehen habe, ist diese in F #. Hoffentlich ist die Syntax kein Hindernis für das Verständnis der Erklärung der Technik.
quelle
Es ist verlockend, den Code zu schreiben und dann zu prüfen, ob das Ergebnis "richtig" aussieht, aber, wie Sie richtig verstehen, ist es keine gute Idee.
Wenn der Algorithmus schwierig ist, können Sie einige Dinge tun, um die manuelle Berechnung des Ergebnisses zu vereinfachen.
Verwenden Sie Excel. Richten Sie eine Tabelle ein, die einen Teil oder die gesamte Berechnung für Sie ausführt. Halten Sie es einfach genug, damit Sie die Schritte sehen können.
Teilen Sie Ihre Methode in kleinere testbare Methoden mit jeweils eigenen Tests auf. Wenn Sie sicher sind, dass die kleineren Teile funktionieren, verwenden Sie sie, um den nächsten Schritt manuell durchzuführen.
Verwenden Sie Aggregateigenschaften zur Überprüfung der Integrität. Angenommen, Sie haben einen Wahrscheinlichkeitsrechner. Sie wissen vielleicht nicht, wie die einzelnen Ergebnisse aussehen sollen, aber Sie wissen, dass sich alle Ergebnisse zu 100% summieren müssen.
Rohe Gewalt. Schreiben Sie ein Programm, das alle möglichen Ergebnisse generiert, und überprüfen Sie, ob keines besser ist als das, was Ihr Algorithmus generiert.
quelle
TL; DR
Lesen Sie den Abschnitt "Vergleichstests", um Ratschläge zu erhalten, die in anderen Antworten nicht enthalten sind.
Anfänge
Beginnen Sie mit dem Testen der Fälle, die vom Algorithmus zurückgewiesen werden sollen (
workPerDay
z. B. null oder negativ ), und der Fälle, die trivial sind (ztasks
. B. leeres Array).Danach möchten Sie zuerst die einfachsten Fälle testen. Für die
tasks
Eingabe müssen wir verschiedene Längen testen; es sollte ausreichen, 0, 1 und 2 Elemente zu testen (2 gehört zu der Kategorie "viele" für diesen Test).Wenn Sie Eingaben finden, die mental berechnet werden können, ist dies ein guter Anfang. Eine Technik, die ich manchmal verwende, ist, von einem gewünschten Ergebnis auszugehen und (in der Spezifikation) zu Eingaben zurückzuarbeiten, die dieses Ergebnis erzeugen sollten.
Vergleichstests
Manchmal ist die Beziehung der Ausgabe zur Eingabe nicht offensichtlich, aber Sie haben eine vorhersehbare Beziehung zwischen verschiedenen Ausgaben, wenn eine Eingabe geändert wird. Wenn ich das Beispiel richtig verstanden habe, erhöht das Hinzufügen einer Aufgabe (ohne andere Eingaben zu ändern) niemals den Anteil der pünktlichen Arbeit, sodass wir einen Test erstellen können, der die Funktion zweimal aufruft - einmal mit und einmal ohne die zusätzliche Aufgabe - und behauptet die Ungleichung zwischen den beiden Ergebnissen.
Fallbacks
Manchmal musste ich auf einen langen Kommentar zurückgreifen, der ein von Hand berechnetes Ergebnis in Schritten anzeigt, die der Spezifikation entsprechen (ein solcher Kommentar ist normalerweise länger als der Testfall). Der schlimmste Fall ist, wenn Sie die Kompatibilität mit einer früheren Implementierung in einer anderen Sprache oder für eine andere Umgebung aufrechterhalten müssen. Manchmal muss man die Testdaten nur mit so etwas wie beschriften
/* derived from v2.6 implementation on ARM system */
. Das ist nicht sehr befriedigend, kann aber als Wiedergabetest beim Portieren oder als kurzfristige Krücke akzeptabel sein.Erinnerungen
Das wichtigste Merkmal eines Tests ist seine Lesbarkeit. Wenn die Ein- und Ausgänge für den Leser undurchsichtig sind, hat der Test einen sehr geringen Wert. Wenn dem Leser jedoch geholfen wird, die Beziehungen zwischen ihnen zu verstehen, dient der Test zwei Zwecken.
Vergessen Sie nicht, für ungenaue Ergebnisse (z. B. Gleitkommazahlen) ein geeignetes "ungefähr gleich" zu verwenden.
Vermeiden Sie übermäßiges Testen - fügen Sie einen Test nur hinzu, wenn er etwas abdeckt (z. B. einen Grenzwert), den andere Tests nicht erreichen.
quelle
Diese schwer zu testende Funktion hat nichts Besonderes. Das Gleiche gilt für Code, der externe Schnittstellen verwendet (z. B. eine REST-API einer Drittanbieteranwendung, die nicht von Ihnen gesteuert wird und mit Sicherheit nicht von Ihrer Testsuite getestet werden kann), oder für eine Drittanbieterbibliothek, bei der Sie sich nicht sicher sind, ob dies der Fall ist genaues Byte-Format der Rückgabewerte).
Es ist durchaus sinnvoll, den Algorithmus einfach für eine vernünftige Eingabe auszuführen, zu überprüfen, was er bewirkt, sicherzustellen, dass das Ergebnis korrekt ist, und die Eingabe und das Ergebnis als Testfall zu kapseln. Sie können dies für einige Fälle tun und erhalten so mehrere Proben. Versuchen Sie, die Eingabeparameter so unterschiedlich wie möglich zu gestalten. Im Falle eines externen API-Aufrufs führen Sie einige Aufrufe für das reale System durch, verfolgen sie mit einem Tool und verspotten sie dann in Ihren Komponententests, um zu sehen, wie Ihr Programm reagiert Der Taskplanungscode wird ausgeführt, manuell überprüft und das Ergebnis in den Tests festgeschrieben.
Dann bringen Sie offensichtlich Randfälle wie (in Ihrem Beispiel) eine leere Liste von Aufgaben mit. Sachen wie diese.
Ihre Testsuite ist möglicherweise nicht so gut wie eine Methode, mit der Sie Ergebnisse leicht vorhersagen können. aber immer noch 100% besser als keine Testsuite (oder nur ein Rauchtest).
Wenn Ihr Problem ist allerdings, dass Sie finden es schwer zu entscheiden , ob ein Ergebnis ist korrekt, dann ist das ein ganz anderes Problem. Angenommen, Sie haben eine Methode, die erkennt, ob eine willkürlich große Zahl eine Primzahl ist. Sie können kaum eine zufällige Zahl darauf werfen und dann einfach "schauen", ob das Ergebnis korrekt ist (vorausgesetzt, Sie können die Primzahl in Ihrem Kopf oder auf einem Blatt Papier nicht bestimmen). In diesem Fall gibt es in der Tat wenig, was Sie tun können - Sie müssen entweder bekannte Ergebnisse erhalten (dh einige große Primzahlen) oder die Funktionalität mit einem anderen Algorithmus implementieren (vielleicht sogar einem anderen Team - die NASA scheint das zu mögen dass) und hoffe, dass, wenn eine der beiden Implementierungen fehlerhaft ist, zumindest der Fehler nicht zu den gleichen falschen Ergebnissen führt.
Wenn dies ein normaler Fall für Sie ist, müssen Sie mit Ihren Anforderungsingenieuren ein gutes Gespräch führen. Wenn sie Ihre Anforderungen nicht auf eine Weise formulieren können, die für Sie einfach (oder überhaupt möglich) zu überprüfen ist, wann wissen Sie dann, ob Sie fertig sind?
quelle
Andere Antworten sind gut, deshalb werde ich versuchen, auf einige Punkte einzugehen, die sie bisher gemeinsam übersehen haben.
Ich habe Software für die Bildverarbeitung mit Synthetic Aperture Radar (SAR) geschrieben (und gründlich getestet). Es ist naturwissenschaftlicher / numerischer Natur (es gibt eine Menge Geometrie, Physik und Mathematik).
Einige Tipps (für allgemeine wissenschaftliche / numerische Tests):
1) Verwenden Sie Inverse. Was ist das
fft
von[1,2,3,4,5]
? Keine Ahnung. Was istifft(fft([1,2,3,4,5]))
? Sollte sein[1,2,3,4,5]
(oder in der Nähe davon, Gleitkommafehler könnten auftauchen). Gleiches gilt für den 2D-Fall.2) Verwenden Sie bekannte Zusicherungen. Wenn Sie eine Determinantenfunktion schreiben, kann es schwierig sein, die Determinante einer zufälligen 100x100-Matrix zu bestimmen. Aber Sie wissen, dass die Determinante der Identitätsmatrix 1 ist, auch wenn sie 100x100 ist. Sie wissen auch, dass die Funktion 0 in einer nicht invertierbaren Matrix zurückgeben sollte (wie eine 100x100, die mit allen 0en gefüllt ist).
3) Verwenden Sie grobe Aussagen anstelle exakter Aussagen. Ich habe einen Code für die SAR-Verarbeitung geschrieben, mit dem zwei Bilder registriert werden, indem Verbindungspunkte generiert werden, die eine Zuordnung zwischen den Bildern erstellen, und anschließend eine Verzerrung zwischen ihnen vorgenommen wird, um eine Übereinstimmung zu erzielen. Es könnte sich auf einer Subpixel-Ebene registrieren. A priori ist es schwer zu sagen , wie die Registrierung von zwei Bildern aussehen könnte. Wie kannst du es testen? Dinge wie:
Da Sie sich nur für überlappende Teile registrieren können, muss das registrierte Bild kleiner oder gleich Ihrem kleinsten Bild sein und außerdem:
Da ein für sich selbst registriertes Bild für sich selbst nahe sein sollte, aufgrund des vorliegenden Algorithmus jedoch möglicherweise mehr als nur Gleitkommafehler auftreten, prüfen Sie einfach, ob sich jedes Pixel innerhalb von +/- 5% des Bereichs befindet, den die Pixel einnehmen können (0-255 ist Graustufen, wie es in der Bildverarbeitung üblich ist). Das Ergebnis sollte mindestens die gleiche Größe wie die Eingabe haben.
Sie können sogar einfach einen Rauchtest durchführen (dh es anrufen und sicherstellen, dass es nicht abstürzt). Im Allgemeinen ist diese Technik besser für größere Tests, bei denen das Endergebnis nicht (leicht) vor dem Ausführen des Tests berechnet werden kann.
4) Verwenden Sie OR STORE einen Zufallszahlen-Startwert für Ihren RNG.
Läuft sie müssen reproduzierbar sein. Es ist jedoch falsch, dass die einzige Möglichkeit, einen reproduzierbaren Lauf zu erhalten, darin besteht, einem Zufallszahlengenerator einen bestimmten Startwert bereitzustellen. Manchmal sind Zufallstests wertvoll. Ich habe Bugs in wissenschaftlichem Code gesehen / gehört, die in entarteten Fällen auftreten, die zufällig generiert wurden (in komplizierten Algorithmen kann es schwierig sein, den entarteten Fall überhaupt zu erkennen)). Anstatt Ihre Funktion immer mit demselben Startwert aufzurufen, generieren Sie einen zufälligen Startwert, verwenden Sie diesen Startwert und protokollieren Sie den Wert des Startwerts. Auf diese Weise hat jeder Lauf einen anderen zufälligen Startwert. Wenn jedoch ein Absturz auftritt, können Sie das Ergebnis erneut ausführen, indem Sie den Startwert verwenden, den Sie zum Debuggen angemeldet haben. Ich habe dies tatsächlich in der Praxis verwendet und es hat einen Fehler beseitigt, also dachte ich mir, ich würde es erwähnen. Zugegeben, das ist nur einmal passiert, und ich bin mir sicher, dass es sich nicht immer lohnt, diese Technik mit Vorsicht anzuwenden. Zufall mit dem gleichen Samen ist jedoch immer sicher. Nachteil (im Gegensatz dazu, dass immer nur derselbe Startwert verwendet wird): Sie müssen Ihre Testläufe protokollieren. Oberseite: Korrektheit und Bugnuking.
Ihr besonderer Fall
1) Prüfen Sie, ob ein leerer
taskArray
Wert 0 zurückgibt (bekannte Zusicherung).2) Erzeugen Sie gelegentlichen Eingang , so dass
task.time > 0
,task.due > 0
, undtask.importance > 0
für alltask
s, und behauptet , das Ergebnis größer als0
(Grob assert, zufällige Input) . Sie müssen nicht verrückt werden und zufällige Samen erzeugen, Ihr Algorithmus ist einfach nicht komplex genug, um dies zu rechtfertigen. Es gibt ungefähr keine Chance, dass es sich auszahlt: Halten Sie den Test einfach.3) Teste ob
task.importance == 0
für alletask
s, dann ist das Ergebnis0
(bekannte Behauptung)4) Andere Antworten haben dies angesprochen, aber es kann für Ihren speziellen Fall wichtig sein : Wenn Sie eine API erstellen, die von Benutzern außerhalb Ihres Teams verwendet werden soll, müssen Sie die entarteten Fälle testen.
workPerDay == 0
Stellen Sie beispielsweise sicher, dass Sie einen schönen Fehler ausgeben, der dem Benutzer mitteilt, dass die Eingabe ungültig ist. Wenn Sie keine API erstellen und diese nur für Sie und Ihr Team ist, können Sie diesen Schritt wahrscheinlich überspringen und es einfach ablehnen, sie mit dem entarteten Fall aufzurufen.HTH.
quelle
Integrieren Sie Assertion-Tests in Ihre Unit-Test-Suite, um Ihren Algorithmus eigenschaftsbasiert zu testen. Zusätzlich zum Schreiben von Komponententests, die auf bestimmte Ausgaben prüfen, können Sie auch Tests schreiben, die fehlschlagen, indem sie Assertionsfehler im Hauptcode auslösen.
Viele Algorithmen stützen sich für ihre Korrektheitsnachweise auf die Aufrechterhaltung bestimmter Eigenschaften während der Stufen des Algorithmus. Wenn Sie diese Eigenschaften sinnvoll überprüfen können, indem Sie sich die Ausgabe einer Funktion ansehen, reicht ein Komponententest aus, um Ihre Eigenschaften zu testen. Andernfalls können Sie beim Assertion-basierten Testen jedes Mal testen, ob eine Implementierung eine Eigenschaft beibehält, wenn der Algorithmus dies annimmt.
Assertion-based Testing wird Algorithmusfehler, Codierungsfehler und Implementierungsfehler aufgrund von Problemen wie numerischer Instabilität aufdecken. In vielen Sprachen werden Assertions beim Kompilieren oder vor der Interpretation des Codes entfernt, damit die Assertions im Produktionsmodus nicht zu Leistungseinbußen führen. Wenn Ihr Code Unit-Tests besteht, in einem realen Fall jedoch fehlschlägt, können Sie die Zusicherungen als Debugging-Tool wieder aktivieren.
quelle
Einige der anderen Antworten hier sind sehr gut:
... ich würde noch ein paar andere Taktiken hinzufügen:
Durch die Zerlegung können Sie sicherstellen, dass die Komponenten Ihres Algorithmus genau das tun, was Sie von ihnen erwarten. Und mit einer "guten" Zersetzung können Sie auch sicherstellen, dass sie richtig zusammengeklebt sind. Eine gute Zerlegung verallgemeinert und vereinfacht den Algorithmus so weit, dass Sie die Ergebnisse (der vereinfachten, generischen Algorithmen) von Hand gut genug vorhersagen können , um gründliche Tests zu schreiben.
Wenn Sie nicht in diesem Ausmaß zerlegen können, beweisen Sie den Algorithmus außerhalb des Codes mit allen Mitteln, die ausreichen, um Sie und Ihre Kollegen, Stakeholder und Kunden zufrieden zu stellen. Zerlegen Sie dann nur so viel, dass Sie nachweisen können, dass Ihre Implementierung mit dem Design übereinstimmt.
quelle
Dies mag wie eine idealistische Antwort erscheinen, hilft jedoch dabei, verschiedene Arten von Tests zu identifizieren.
Wenn strenge Antworten für die Implementierung wichtig sind, sollten Beispiele und erwartete Antworten in den Anforderungen angegeben werden, die den Algorithmus beschreiben. Diese Anforderungen sollten einer Gruppenüberprüfung unterzogen werden. Wenn Sie nicht dieselben Ergebnisse erhalten, muss der Grund ermittelt werden.
Auch wenn Sie sowohl als Analyst als auch als Implementierer auftreten, sollten Sie Anforderungen erstellen und überprüfen lassen, lange bevor Sie Komponententests schreiben. In diesem Fall kennen Sie die erwarteten Ergebnisse und können Ihre Tests entsprechend schreiben.
Wenn dies jedoch ein Teil ist, den Sie implementieren, der entweder nicht Teil der Geschäftslogik ist oder der eine Geschäftslogikantwort unterstützt, sollte es in Ordnung sein, den Test auszuführen, um die Ergebnisse zu ermitteln und dann den zu erwartenden Test zu ändern diese Ergebnisse. Die Endergebnisse werden bereits mit Ihren Anforderungen verglichen. Wenn sie also korrekt sind, muss der gesamte Code, der diese Endergebnisse liefert, numerisch korrekt sein. An diesem Punkt dienen Ihre Komponententests eher zum Erkennen von Randversagensfällen und künftigen Umgestaltungsänderungen als zum Nachweis, dass dies gegeben ist Algorithmus erzeugt korrekte Ergebnisse.
quelle
Ich denke, es ist durchaus akzeptabel, den Prozess gelegentlich zu verfolgen:
Dies ist ein vernünftiger Ansatz in jeder Situation, in der es einfacher ist, die Richtigkeit einer Antwort von Hand zu überprüfen, als die Antwort von Hand nach den ersten Prinzipien zu berechnen.
Ich kenne Leute, die Software zum Rendern von gedruckten Seiten schreiben und Tests durchführen, die überprüfen, ob auf der gedruckten Seite genau die richtigen Pixel eingestellt sind. Der einzig vernünftige Weg, dies zu tun, besteht darin, den Code zum Rendern der Seite zu schreiben, mit den Augen zu überprüfen, ob sie gut aussieht, und das Ergebnis dann als Regressionstest für zukünftige Versionen zu erfassen.
Nur weil Sie in einem Buch gelesen haben, dass eine bestimmte Methodik dazu anregt, zuerst die Testfälle zu schreiben, müssen Sie dies nicht immer so tun. Regeln sind da, um gebrochen zu werden.
quelle
Bei anderen Antworten gibt es bereits Techniken, wie ein Test aussieht, wenn das spezifische Ergebnis nicht außerhalb der getesteten Funktion ermittelt werden kann.
Was ich zusätzlich tue, was ich in den anderen Antworten nicht gesehen habe, ist, Tests auf irgendeine Weise automatisch zu generieren:
Wenn die Funktion beispielsweise drei Parameter mit jeweils einem zulässigen Eingabebereich [-1,1] akzeptiert, testen Sie alle Kombinationen der einzelnen Parameter {-2, -1,01, -1, -0,99, -0,5, -0,01, 0,0,01 0,5, 0, 0, 99, 1, 01, 2, etwas mehr zufällig in (-1, 1)}
Kurz gesagt: Manchmal kann schlechte Qualität durch Quantität subventioniert werden.
quelle