Hätten Unit-Tests der Citigroup geholfen, diesen teuren Fehler zu vermeiden?

86

Ich habe über dieses Problem gelesen: Der Programmierfehler kostet Citigroup $ 7 Millionen nach legitimen Transaktionen, die 15 Jahre lang für Testdaten gehalten wurden .

Als das System Mitte der neunziger Jahre eingeführt wurde, filterte der Programmcode alle Transaktionen heraus, denen dreistellige Verzweigungscodes von 089 bis 100 zugewiesen wurden, und verwendete diese Präfixe zu Testzwecken.

Aber im Jahr 1998 begann das Unternehmen, alphanumerische Filialcodes zu verwenden, als es sein Geschäft ausbaute. Darunter befanden sich die Codes 10B, 10C usw., die vom System als innerhalb des ausgeschlossenen Bereichs liegend behandelt wurden, und so wurden ihre Transaktionen aus allen an die SEC gesendeten Berichten entfernt.

(Ich denke, dies zeigt, dass die Verwendung eines nicht expliziten Datenindikators ... suboptimal ist. Es wäre viel besser gewesen, eine semantisch explizite Branch.IsLiveEigenschaft aufzufüllen und zu verwenden .)

Abgesehen davon war meine erste Reaktion "Unit-Tests hätten hier geholfen" ... aber würden sie?

Ich habe kürzlich gelesen, warum die meisten Komponententests mit Interesse verschwendet werden und meine Frage lautet: Wie würden die Komponententests aussehen, die bei der Einführung von alphanumerischen Verzweigungscodes fehlgeschlagen wären?

Matt Evans
quelle
17
Offenbar haben sie auch einen Integrationstest verpasst, bei dem die Anzahl der in SEC exportierten Transaktionen überprüft wurde. Wenn Sie eine Exportfunktion erstellen, wäre dies eine vernünftige Prüfung.
Luc Franken
31
Der Autor des Artikels scheint Unit-Tests nicht zu verstehen. Einige Behauptungen sind einfach lächerlich ( "Unit-Tests werden wahrscheinlich nicht mehr als ein Billionstel der Funktionalität einer bestimmten Methode testen" ), andere würden die Chance auf Regressionen zerstören ( "Sehen Sie sich die Tests an, die seit einem Jahr und nie fehlgeschlagen sind erwägen, sie wegzuwerfen " ). Oder Vorschläge wie "Unit-Tests in Assertions verwandeln" , die fehlgeschlagene Tests für Laufzeitausnahmen ändern sollen?
Groo
25
@gnat Ich habe den externen Link nicht gelesen und fand diese Frage dennoch aussagekräftig
Jeutnarg
23
Für das, was es wert ist, bin ich ziemlich anderer Meinung als alles in "Warum die meisten Unit-Tests Verschwendung sind". Ich würde eine Gegenargumentation schreiben, aber dieser Rand ist zu klein, um ihn zu enthalten.
Robert Harvey

Antworten:

19

Fragen Sie sich wirklich: "Hätten Unit-Tests hier geholfen?", Oder fragen Sie sich: "Könnten irgendwelche Tests hier möglicherweise geholfen haben?".

Die naheliegendste Form des Testens, die geholfen hätte, ist die Voraussetzung im Code selbst, dass eine Verzweigungskennung nur aus Ziffern besteht (vorausgesetzt, dies ist die Annahme, auf die sich der Codierer beim Schreiben des Codes stützt).

Dies könnte dann in einer Art Integrationstest fehlgeschlagen sein, und sobald die neuen alphanumerischen Verzweigungs-IDs eingeführt werden, explodiert die Behauptung. Aber das ist kein Komponententest.

Alternativ könnte es einen Integrationstest des Verfahrens geben, der den SEC-Bericht generiert. Dieser Test stellt sicher, dass jede reale Zweigstellenkennung ihre Transaktionen meldet (und erfordert daher eine reale Eingabe, eine Liste aller verwendeten Zweigstellenkennungen). Das ist also auch kein Unit-Test.

Ich kann keine Definition oder Dokumentation der beteiligten Schnittstellen sehen, aber es kann sein, dass Unit-Tests den Fehler möglicherweise nicht erkannt haben, weil das Gerät nicht fehlerhaft war . Wenn das Gerät aus diesem Zweig Bezeichner ist nur zulässig , von Ziffern zu übernehmen, und die Entwickler machten nie eine Entscheidung , was der Code für den Fall tun soll es nicht, dann sie sollten nichtSchreiben Sie einen Komponententest, um ein bestimmtes Verhalten bei nicht-stelligen Kennungen zu erzwingen, da der Test eine hypothetisch gültige Implementierung der Einheit ablehnen würde, die alphanumerische Verzweigungskennungen korrekt behandelt, und Sie normalerweise keinen Komponententest schreiben möchten, der die Gültigkeit verhindert zukünftige Implementierungen und Erweiterungen. Oder vielleicht hat ein Dokument, das vor 40 Jahren geschrieben wurde, implizit (über einen lexikografischen Bereich in EBCDIC-Rohdaten anstelle einer menschlicheren Kollatierungsregel) festgelegt, dass 10B eine Testkennung ist, da sie tatsächlich zwischen 089 und 100 liegt. Aber dann Vor 15 Jahren hat sich jemand entschieden, es als echte Kennung zu verwenden, damit der "Fehler" nicht in der Einheit liegt, die die ursprüngliche Definition korrekt implementiert: es liegt in dem Prozess, der nicht bemerkt hat, dass 10B als Testidentifikator definiert ist und daher keiner Verzweigung zugewiesen werden sollte. Dasselbe würde in ASCII passieren, wenn Sie 089 - 100 als Testbereich definieren und dann einen Bezeichner 10 $ oder 1.0 eingeben. Es kommt einfach vor, dass in EBCDIC die Ziffern hinter den Buchstaben stehen.

Ein Unit-Test (oder wohl ein Funktionstest), der vorstellbar istVielleicht haben Sie den Tag gerettet, ist ein Test des Geräts, das neue Zweig-IDs generiert oder validiert. Dieser Test würde behaupten, dass die Bezeichner nur Ziffern enthalten dürfen und geschrieben werden, damit Benutzer der Zweigbezeichner dasselbe annehmen können. Oder vielleicht gibt es irgendwo eine Einheit, die echte Verzweigungskennungen importiert, aber die Testkennungen nie sieht, und die von der Einheit getestet werden kann, um sicherzustellen, dass alle Testkennungen abgelehnt werden (wenn die Kennungen nur drei Zeichen umfassen, können wir sie alle auflisten und das Verhalten von vergleichen den Validator des Testfilters, um sicherzustellen, dass sie übereinstimmen, was die üblichen Einwände gegen Stichproben betrifft). Wenn dann jemand die Regeln geändert hat, wäre der Komponententest fehlgeschlagen, da er dem neu erforderlichen Verhalten widerspricht.

Da der Test aus gutem Grund vorhanden war, bietet sich für jemanden die Möglichkeit, ihn aufgrund geänderter Geschäftsanforderungen zu entfernen. "Suchen Sie jeden Punkt im Code, der sich auf das gewünschte Verhalten stützt Veränderung". Dies ist natürlich schwierig und daher unzuverlässig, so dass die Rettung des Tages keineswegs garantiert ist. Wenn Sie jedoch Ihre Annahmen in Tests der Einheiten erfassen, für die Sie Eigenschaften annehmen, haben Sie sich eine Chance gegeben, und der Aufwand wird nicht vollständig verschwendet.

Ich stimme natürlich zu, dass es nichts zu testen gibt, wenn die Einheit nicht an erster Stelle mit einem "witzig geformten" Eingang definiert worden wäre. Fummelige Namespace-Abteilungen können schwierig zu testen sein, da die Schwierigkeit nicht darin besteht, Ihre lustige Definition zu implementieren, sondern sicherzustellen, dass jeder Ihre lustige Definition versteht und respektiert. Das ist keine lokale Eigenschaft einer Codeeinheit. Darüber hinaus entspricht das Ändern eines Datentyps von "einer Zeichenfolge" in "eine Zeichenfolge aus alphanumerischen Zeichen" dem Verarbeiten von Unicode durch ein ASCII-basiertes Programm: Es ist nicht einfach, wenn Ihr Code stark an die ursprüngliche Definition gekoppelt ist und wann Der Datentyp ist grundlegend für das, was das Programm tut, und ist dann oft stark gekoppelt.

Es ist ein bisschen beunruhigend zu glauben, dass es größtenteils vergebliche Anstrengungen sind

Wenn Ihre Komponententests manchmal fehlschlagen (z. B. beim Refactoring) und dabei nützliche Informationen liefern (z. B. ist Ihre Änderung falsch), wurde der Aufwand nicht verschwendet. Was sie nicht tun, ist zu testen, ob Ihr System funktioniert. Wenn Sie also Unit-Tests schreiben, anstatt Funktions- und Integrationstests durchzuführen, nutzen Sie Ihre Zeit möglicherweise nicht optimal.

Steve Jessop
quelle
Behauptungen sind gut!
3
@nocomprende: Wie Reagan es hatte, "vertraue, aber überprüfe".
Steve Jessop
1
Ich wollte auch sagen "Unit tests bad!" Aber ich dachte, die meisten Leute würden den Verweis auf Animal Farm verpassen und anfangen, mich zu kritisieren , anstatt darüber nachzudenken, was ich sagte (ruckartige Reaktionen sind unwirksam), aber das habe ich nicht gesagt. Vielleicht kann eine Person, die klüger ist und eine bessere Gelehrsamkeit hat, diesen Punkt klarstellen.
2
"Alle Tests bestehen, aber einige Tests bestehen MEHR als andere!"
Graham
1
Testen ist ein roter Hering. Diese Leute wussten einfach nicht, wie "Branch Code" definiert wurde. Dies wäre, als wüsste das US-Postamt nicht, dass es die Definition der Postleitzahl geändert hat, als es 4 Ziffern hinzufügte.
Radarbob
120

Unit-Tests hätten feststellen können, dass die Filialcodes 10B und 10C fälschlicherweise als "Testfilialen" klassifiziert wurden, aber ich halte es für unwahrscheinlich, dass die Tests für diese Filialklassifizierung umfangreich genug gewesen wären, um diesen Fehler abzufangen.

Andererseits hätten Stichproben der generierten Berichte ergeben können, dass verzweigte 10B und 10C in den Berichten häufig fehlten, viel eher als in den 15 Jahren, in denen der Fehler nun vorhanden sein durfte.

Schließlich ist dies ein gutes Beispiel dafür, warum es eine schlechte Idee ist, Testdaten mit den realen Produktionsdaten in einer Datenbank zu mischen. Wenn sie eine separate Datenbank verwendet hätten, die die Testdaten enthält, wäre es nicht notwendig gewesen, dies aus den offiziellen Berichten herauszufiltern, und es wäre unmöglich gewesen, zu viel herauszufiltern.

Bart van Ingen Schenau
quelle
80
+1 Unit-Tests können nie für schlechte Design-Entscheidungen (wie Mischen Test und reale Daten) zu
kompensieren
5
Während es am besten ist, das Mischen von Testdaten mit realen Daten zu vermeiden, kann es schwierig sein, ein Produktionssystem zu validieren, wenn dies das Ändern von realen Daten erfordert. Zum Beispiel ist es eine schlechte Idee, ein Bankensystem durch Ändern der Bankkontensummen in der Produktion zu validieren. Die Verwendung von Codebereichen zur Bezeichnung von Bedeutungen ist problematisch. Ein expliziteres Attribut der Datensätze wäre wahrscheinlich die bessere Wahl gewesen.
JimmyJames
4
@Voo Ich denke, es gibt eine implizite Annahme, dass es ein Maß an Komplexität oder Zuverlässigkeit gibt, bei dem das Testen des tatsächlich eingesetzten Produktionssystems als sinnvoll oder notwendig erachtet wird. (Überlegen Sie, wie viel aufgrund einer falschen Konfigurationsvariablen schief gehen könnte.) Dies könnte für ein großes Finanzinstitut der Fall sein.
jpmc26
4
@Voo Ich spreche nicht über das Testen. Ich spreche über die Validierung des Systems. In einem realen Produktionssystem gibt es viele Möglichkeiten, bei denen Fehler auftreten können, die nichts mit Code zu tun haben. Wenn Sie ein neues Bankensystem in Betrieb nehmen, liegt möglicherweise ein Problem in der Datenbank oder im Netzwerk usw. vor, das verhindert, dass Transaktionen auf die Konten angewendet werden. Ich habe noch nie bei einer Bank gearbeitet, aber ich bin mir ziemlich sicher, dass es verpönt ist, echte Konten mit falschen Transaktionen zu modifizieren. Sie können also entweder falsche Konten einrichten oder abwarten und beten.
JimmyJames
12
@JimmyJames Im Gesundheitswesen ist es üblich, die Produktionsdatenbank regelmäßig in die Testumgebung zu kopieren, um Tests an Daten durchzuführen, die so realitätsnah wie möglich sind. Ich denke, eine Bank kann das auch.
18.
75

Die Software musste bestimmte Geschäftsregeln erfüllen. Wenn es Komponententests gegeben hätte, hätten die Komponententests überprüft, ob die Software die Geschäftsregeln korrekt handhabt.

Die Geschäftsregeln haben sich geändert.

Anscheinend bemerkte niemand, dass sich die Geschäftsregeln geändert hatten, und niemand änderte die Software, um die neuen Geschäftsregeln anzuwenden. Wenn es Unit-Tests gegeben hätte, müssten diese Unit-Tests geändert werden, aber niemand hätte das getan, weil niemand bemerkt hätte, dass sich die Geschäftsregeln geändert hatten.

Nein, Unit-Tests hätten das nicht erfasst.

Die Ausnahme wäre, wenn die Komponententests und die Software von unabhängigen Teams erstellt wurden und das Team, das die Komponententests durchführt, die Tests geändert hat, um die neuen Geschäftsregeln anzuwenden. Dann wären die Unit-Tests fehlgeschlagen, was hoffentlich zu einer Änderung der Software geführt hätte.

Im selben Fall würden natürlich auch die Unit-Tests fehlschlagen, wenn nur die Software und nicht die Unit-Tests geändert würden. Wenn ein Komponententest fehlschlägt, bedeutet dies nicht, dass die Software falsch ist, sondern, dass entweder die Software oder der Komponententest (manchmal beide) falsch sind.

gnasher729
quelle
2
Ist es möglich, verschiedene Teams zu haben, in denen eines an Code und das andere an "Unit" -Tests arbeitet? Wie ist das überhaupt möglich? ... Ich überarbeite ständig meinen Code.
Sergio
2
@Sergio aus einer Perspektive: Durch das Refactoring werden Änderungen an Interna vorgenommen, während das Verhalten beibehalten wird. Wenn der Test also so geschrieben ist, dass das Verhalten überprüft wird, ohne auf Interna angewiesen zu sein, muss er nicht aktualisiert werden.
Daenyth
1
Ich habe das schon einige Male gesehen. Software wird ohne Beanstandungen produziert, dann explodieren die Benutzer plötzlich mit der Beanstandung, dass sie nicht mehr funktioniert und im Laufe der Jahre nach und nach versagt hat. Das passiert, wenn Sie sich entscheiden, Ihre internen Abläufe zu ändern, ohne den Standard-Benachrichtigungsprozess zu befolgen ...
Brian Knoblauch
42
"Die Geschäftsregeln haben sich geändert", lautet die kritische Beobachtung. Unit-Tests bestätigen, dass Sie die Logik implementiert haben, von der Sie dachten , dass Sie sie implementiert haben , und nicht, dass Ihre Logik korrekt war .
Ryan Cavanaugh
5
Wenn ich richtig bin, was passiert ist, ist es unwahrscheinlich, dass Unit-Tests geschrieben werden, um dies zu erfassen. Das Grundprinzip für die Auswahl von Tests besteht darin, einige "gute" Fälle, einige "schlechte" Fälle und Fälle zu testen, die irgendwelche Grenzen überschreiten. In diesem Fall würden Sie "099", "100" und "101" testen. Da "10B" unter dem alten System von den Tests "Nicht-Zahlen ablehnen" abgedeckt wurde und unter dem neuen System größer als 101 ist (und daher auch von diesen Tests abgedeckt wird), gibt es keinen Grund, es zu testen - außer in EBCDIC, "10B" sortiert zwischen "099" und "100".
Mark
29

Nein. Dies ist eines der großen Probleme bei Unit-Tests: Sie wiegen Sie in ein falsches Sicherheitsgefühl.

Wenn alle Ihre Tests bestanden sind, bedeutet dies nicht, dass Ihr System ordnungsgemäß funktioniert. Es bedeutet, dass alle Ihre Tests bestanden werden . Das bedeutet, dass die Teile Ihres Designs, für die Sie sich bewusst Gedanken gemacht und Tests geschrieben haben, so funktionieren, wie Sie es sich bewusst vorgestellt haben, was ohnehin keine große Sache ist Es ist also sehr wahrscheinlich, dass Sie es trotzdem richtig verstanden haben! Aber es tut nichts, um Fälle aufzufangen, an die Sie noch nie gedacht haben, wie diese, weil Sie nie daran gedacht haben, einen Test für sie zu schreiben. (Und wenn Sie dies getan hätten, wären Sie sich dessen bewusst gewesen, dass Codeänderungen erforderlich waren, und Sie hätten sie geändert.)

Mason Wheeler
quelle
17
Mein Vater fragte mich immer: Warum hast du nicht an das gedacht, woran du nicht gedacht hast? (Nur er machte es immer verwirrend, indem er sagte: "Wenn Sie es nicht wissen, fragen Sie !") Aber woher weiß ich, dass ich es nicht weiß?
7
"Es bedeutet, dass die Teile Ihres Designs, für die Sie bewusst nachgedacht und Tests geschrieben haben, so funktionieren, wie Sie es sich bewusst vorgestellt haben." Genau richtig. Diese Informationen sind von unschätzbarem Wert, wenn Sie überarbeiten oder wenn sich an einer anderen Stelle im System etwas ändert, das Ihre Annahmen verletzt. Entwickler, die sich in ein falsches Sicherheitsgefühl wiegen, verstehen die Einschränkungen von Unit-Tests einfach nicht, aber dies macht Unit-Tests nicht zu einem nutzlosen Tool.
Robert Harvey
12
@ MasonWheeler: Wie Sie denkt der Autor, dass Unit-Tests irgendwie beweisen sollen, dass Ihr Programm funktioniert. Das tut es nicht. Lassen Sie mich das wiederholen: Unit-Tests beweisen nicht, dass Ihr Programm funktioniert. Unit-Tests beweisen, dass Ihre Methoden Ihren Testvertrag erfüllen, und das ist alles, was es tut. Der Rest des Papiers fällt runter, weil es auf dieser einzigen ungültigen Prämisse beruht.
Robert Harvey
5
Natürlich werden Entwickler, die diese falsche Überzeugung haben, enttäuscht sein, wenn ein Unit-Test völlig fehlschlägt, aber das ist die Schuld des Entwicklers, nicht der Unit-Tests, und es macht den echten Wert, den ein Unit-Test bietet, nicht ungültig.
Robert Harvey
5
o_O @ dein erster Satz. Unit-Tests geben Ihnen ein falsches Sicherheitsgefühl beim Codieren, als hätten Sie Ihre Hände am Lenkrad, und geben Ihnen ein falsches Sicherheitsgefühl beim Fahren.
Djechlin
10

Nein, nicht unbedingt.

Die ursprüngliche Anforderung bestand darin, numerische Verzweigungscodes zu verwenden, sodass ein Einheitentest für eine Komponente erstellt worden wäre, die verschiedene Codes akzeptierte und etwa 10B ablehnte. Das System wäre als funktionierend übergeben worden (was es war).

Dann hätte sich die Anforderung geändert und die Codes aktualisiert, aber dies hätte zur Folge gehabt, dass der Unit-Test-Code, der die fehlerhaften Daten lieferte (das sind jetzt gute Daten), geändert werden müsste.

Nun nehmen wir an, dass die Leute, die das System verwalten, dies wissen und den Komponententest ändern würden, um die neuen Codes zu handhaben ... aber wenn sie gewusst hätten, dass dies eintrifft, hätten sie auch gewusst, dass der Code, der diese handhabt, geändert wird Codes sowieso .. und sie haben das nicht getan. Ein Komponententest, der ursprünglich Code 10B abgelehnt hat, hätte beim Ausführen gerne "alles in Ordnung" gesagt, wenn Sie nicht gewusst hätten, diesen Test zu aktualisieren.

Unit-Tests eignen sich gut für die ursprüngliche Entwicklung, aber nicht für Systemtests, insbesondere nicht 15 Jahre nach langem Vergessen der Anforderungen.

Was sie in einer solchen Situation brauchen, ist ein durchgehender Integrationstest. Hier können Sie die erwarteten Daten übergeben und prüfen, ob dies der Fall ist. Jemand hätte bemerkt, dass die neuen Eingabedaten keinen Bericht ergaben, und würde dann weitere Nachforschungen anstellen.

gbjbaanb
quelle
Spot on. Und das Hauptproblem (nur?) Bei Unit-Tests. Habe mich gerettet, meine eigene Antwort zu formulieren, da ich genau dasselbe gesagt hätte (aber wahrscheinlich noch schlimmer!) :)
Leichtigkeitsrennen im Orbit
8

Typentests (der Prozess des Testens von Invarianten mit zufällig generierten gültigen Daten, wie beispielsweise die Haskell- Testbibliothek QuickCheck und verschiedene in anderen Sprachen inspirierte Ports / Alternativen) haben dieses Problem möglicherweise behoben, Unit-Tests hätten dies mit ziemlicher Sicherheit nicht getan .

Dies liegt daran, dass bei der Aktualisierung der Regeln für die Gültigkeit von Zweigstellencodes wahrscheinlich niemand daran gedacht hätte, diese spezifischen Bereiche zu testen, um sicherzustellen, dass sie ordnungsgemäß funktionieren.

Wenn jedoch ein Typentest verwendet wurde, sollte jemand zum Zeitpunkt der Implementierung des ursprünglichen Systems ein Paar von Eigenschaften geschrieben haben, um zu überprüfen, ob die spezifischen Codes für Testzweige als Testdaten behandelt wurden, und um zu überprüfen, ob keine anderen Codes vorhanden waren Wäre ..., wenn die Datentypdefinition für den Verzweigungscode aktualisiert worden wäre (was erforderlich gewesen wäre, um zu testen, ob eine der Änderungen für den Verzweigungscode von Ziffer zu Ziffer funktioniert hat), hätte dieser Test begonnen, Werte in zu testen der neue Bereich und hätte höchstwahrscheinlich den Fehler identifiziert.

Natürlich wurde QuickCheck erstmals im Jahr 1999 entwickelt, sodass es für dieses Problem bereits zu spät war.

Jules
quelle
1
Ich denke, es ist normaler, diese Eigenschafts-basierten Tests zu nennen, und natürlich ist es auch möglich, einen Eigenschafts-basierten Test zu schreiben, der bei dieser Änderung immer noch erfolgreich ist (obwohl ich glaube, dass Sie mit größerer Wahrscheinlichkeit einen Test schreiben, der dies findet).
jk.
5

Ich bezweifle wirklich, dass Unit-Tests dieses Problem lösen könnten. Es klingt wie eine dieser Tunnelblicksituationen, da die Funktionalität geändert wurde, um neue Verzweigungscodes zu unterstützen, dies jedoch nicht in allen Bereichen des Systems durchgeführt wurde.

Wir verwenden Unit-Tests, um eine Klasse zu entwerfen. Ein erneutes Ausführen eines Komponententests ist nur erforderlich, wenn sich das Design geändert hat. Wenn sich eine bestimmte Einheit nicht ändert, liefern die unveränderten Einheitentests die gleichen Ergebnisse wie zuvor. Komponententests zeigen Ihnen nicht die Auswirkungen von Änderungen an anderen Einheiten (wenn dies der Fall ist, schreiben Sie keine Komponententests).

Sie können dieses Problem nur auf folgende Weise erkennen:

  • Integrationstests - Sie müssten jedoch die neuen Codeformate speziell hinzufügen, um mehrere Einheiten im System zu durchlaufen (dh sie würden Ihnen das Problem nur dann anzeigen, wenn die ursprünglichen Tests die jetzt gültigen Zweige enthalten).
  • End-to-End-Tests - Das Unternehmen sollte einen End-to-End-Test durchführen, bei dem alte und neue Branchencodeformate berücksichtigt werden

Das Fehlen ausreichender End-to-End-Tests ist besorgniserregender. Sie können sich nicht auf Unit-Tests als NUR- oder HAUPT-Test für Systemänderungen verlassen. Klingt so, als müsste nur jemand einen Bericht über die neu unterstützten Zweigcodeformate erstellen.

Klassenskelett
quelle
2

Eine in die Laufzeit integrierte Behauptung könnte geholfen haben; zum Beispiel:

  1. Erstellen Sie eine Funktion wie bool isTestOnly(string branchCode) { ... }
  2. Verwenden Sie diese Funktion, um zu entscheiden, welche Berichte herausgefiltert werden sollen
  3. Verwenden Sie diese Funktion in einer Zusicherung im Code zum Erstellen von Zweigen erneut, um zu überprüfen oder zu bestätigen, dass ein Zweig nicht mit dieser Art von Zweigcode erstellt wurde (werden kann).
  4. Haben Sie diese Zusicherung in der Echtzeit aktiviert (und nicht "optimiert, außer in der Debug-only-Entwicklerversion des Codes")?

Siehe auch:

ChrisW
quelle
2

Die Konsequenz daraus ist, schnell zu scheitern .

Wir haben weder den Code noch viele Beispiele für Präfixe, die gemäß dem Code Testzweigpräfixe sind oder nicht. Wir haben nur Folgendes:

  • 089 - 100 => Testzweig
  • 10B, 10C => Testzweig
  • <088 => vermutlich echte Zweige
  • > 100 => vermutlich echte Zweige

Die Tatsache, dass der Code Zahlen und Zeichenfolgen zulässt, ist mehr als ein bisschen seltsam. Natürlich können 10B und 10C als Hexadezimalzahlen betrachtet werden, aber wenn die Präfixe alle als Hexadezimalzahlen behandelt werden, liegen 10B und 10C außerhalb des Testbereichs und würden als echte Verzweigungen behandelt.

Dies bedeutet wahrscheinlich, dass das Präfix als Zeichenfolge gespeichert, in einigen Fällen jedoch als Zahl behandelt wird. Der einfachste Code, den ich mir vorstellen kann, repliziert dieses Verhalten (mit C # zur Veranschaulichung):

bool IsTest(string strPrefix) {
    int iPrefix;
    if(int.TryParse(strPrefix, out iPrefix))
        return iPrefix >= 89 && iPrefix <= 100;
    return true; //here is the problem
}

Wenn die Zeichenfolge auf Englisch eine Zahl ist und zwischen 89 und 100 liegt, handelt es sich um einen Test. Wenn es keine Zahl ist, ist es ein Test. Ansonsten ist es kein Test.

Wenn der Code diesem Muster folgt, hätte dies zum Zeitpunkt der Bereitstellung des Codes kein Komponententest festgestellt. Hier sind einige Beispiel-Unit-Tests:

assert.isFalse(IsTest("088"))
assert.isTrue(IsTest("089"))
assert.isTrue(IsTest("095"))
assert.isTrue(IsTest("100"))
assert.isFalse(IsTest("101"))
assert.isTrue(IsTest("10B")) // <--- business rule change

Der Komponententest zeigt, dass "10B" als Testzweig behandelt werden sollte. User @ gnasher729 oben sagt, dass sich die Geschäftsregeln geändert haben und das zeigt die letzte Behauptung oben. Irgendwann hätte diese Behauptung zu einer wechseln sollen isFalse, aber das ist nicht geschehen. Unit-Tests werden zur Entwicklungs- und Build-Zeit ausgeführt, jedoch zu keinem Zeitpunkt danach.


Was ist die Lektion hier? Der Code muss auf irgendeine Weise signalisieren, dass er unerwartete Eingaben erhalten hat. Hier ist eine alternative Methode zum Schreiben dieses Codes, mit der hervorgehoben wird, dass das Präfix eine Zahl sein soll:

// Alternative A
bool TryGetIsTest(string strPrefix, out bool isTest) {
    int iPrefix;
    if(int.TryParse(strPrefix, out iPrefix)) {
        isTest = iPrefix >= 89 && iPrefix <= 100;
        return true;
    }
    isTest = true; //this is just some value that won't be read
    return false;
}

Für diejenigen, die C # nicht kennen, gibt der Rückgabewert an, ob der Code ein Präfix aus der angegebenen Zeichenfolge analysieren konnte oder nicht. Wenn der Rückgabewert true ist, kann der aufrufende Code mit der Variablen isTest out prüfen, ob das Zweigpräfix ein Testpräfix ist. Wenn der Rückgabewert false ist, sollte der aufrufende Code melden, dass das angegebene Präfix nicht erwartet wird, und die Variable isTest out ist bedeutungslos und sollte ignoriert werden.

Wenn Sie mit Ausnahmen einverstanden sind, können Sie dies stattdessen tun:

// Alternative B
bool IsTest(string strPrefix) {
    int iPrefix = int.Parse(strPrefix);
    return iPrefix >= 89 && iPrefix <= 100;
}

Diese Alternative ist einfacher. In diesem Fall muss der aufrufende Code die Ausnahme abfangen. In beiden Fällen sollte der Code eine Möglichkeit haben, dem Aufrufer zu melden, dass er kein strPrefix erwartet hat, das nicht in eine Ganzzahl konvertiert werden konnte. Auf diese Weise versagt der Code schnell und die Bank kann das Problem schnell finden, ohne dass es der SEC peinlich ist.

user2023861
quelle
1

So viele Antworten und nicht einmal ein Zitat von Dijkstra:

Tests zeigen das Vorhandensein, nicht das Fehlen von Fehlern.

Daher kommt es darauf an. Wenn der Code richtig getestet wurde, würde dieser Fehler höchstwahrscheinlich nicht existieren.

BЈовић
quelle
-1

Ich denke, ein Unit-Test hier hätte dafür gesorgt, dass das Problem überhaupt nicht existiert.

Bedenken Sie, Sie haben die bool IsTestData(string branchCode)Funktion geschrieben.

Der erste Unit-Test, den Sie schreiben, sollte eine leere und eine leere Zeichenfolge sein. Dann für falsche Längenzeichenfolgen dann für nicht ganzzahlige Zeichenfolgen.

Um alle diese Tests zu bestehen, müssen Sie der Funktion eine Parameterprüfung hinzufügen.

Selbst wenn Sie dann nur auf 'gute' Daten prüfen, 001 -> 999, ohne an die Möglichkeit von 10A zu denken, zwingt Sie die Parameterprüfung, die Funktion neu zu schreiben, wenn Sie mit der Verwendung von alphanumerischen Zeichen beginnen, um die Ausnahmen zu vermeiden, die sie auslösen

Ewan
quelle
1
Dies hätte nicht geholfen - die Funktion wurde nicht geändert, und der Test würde bei den gleichen Testdaten nicht fehlschlagen. Jemand hätte daran gedacht, den Test zu ändern, um ihn zum Scheitern zu bringen, aber wenn er daran gedacht hätte, hätte er wahrscheinlich auch daran gedacht, die Funktion zu ändern.
Hulk
(Oder vielleicht fehlt mir etwas, da ich nicht sicher bin, was Sie mit "Parameterprüfung" meinen)
Hulk
Die Funktion würde gezwungen sein, eine Ausnahme für nicht ganzzahlige Zeichenfolgen auszulösen, um den einfachen Kantenfall-Einheitentest zu bestehen. Daher würde der Produktionscode fehlerhaft sein, wenn Sie damit beginnen würden, alphanumerische Verzweigungscodes ohne spezielle Programmierung zu verwenden
Ewan,
Aber hätte die Funktion IsValidBranchCodefür diese Prüfung nicht eine Funktion verwendet ? Und diese Funktion wäre wahrscheinlich geändert worden, ohne dass das geändert werden müsste IsTestData? Wenn Sie also nur "gute Daten" testen, hätte der Test nicht geholfen. Der Edge-Case-Test hätte einige jetzt gültige (und nicht nur einige noch ungültige) Verzweigungscodes enthalten müssen, um einen Fehler zu verursachen.
Hulk
1
Wenn sich die Prüfung in IsValidCode befindet, so dass die Funktion ohne ihre eigene explizite Prüfung besteht, dann ist es ja möglich, dass sie verpasst wird, aber dann hätten wir einen zusätzlichen Satz von noch mehr Tests, Scheinvalidatoren usw. und noch mehr Chancen für bestimmte "diese sind Testnummern "
Ewan