Czy testy jednostkowe pomogłyby Citigroup uniknąć tego kosztownego błędu?

86

Czytałem o tym snafu: Błąd programowania kosztuje Citigroup 7 milionów dolarów po legalnych transakcjach pomylonych z danymi testowymi przez 15 lat .

Kiedy system został wprowadzony w połowie lat 90., kod programu odfiltrował wszelkie transakcje, którym nadano trzycyfrowe kody od 089 do 100 i wykorzystał te prefiksy do celów testowych.

Ale w 1998 r. Firma zaczęła używać alfanumerycznych kodów branżowych, rozszerzając swoją działalność. Wśród nich były kody 10B, 10C i tak dalej, które system traktował jako znajdujące się w wykluczonym zakresie, a zatem ich transakcje zostały usunięte z raportów wysłanych do SEC.

(Myślę, że to pokazuje, że użycie nieprecyzyjnego wskaźnika danych jest ... nieoptymalne. Lepiej byłoby wypełnić i użyć Branch.IsLivewłaściwości semantycznie jawnej .)

Poza tym moją pierwszą reakcją było „Testy jednostkowe by tu pomogły”… ale czy tak?

Niedawno przeczytałem, dlaczego większość testów jednostkowych jest marnotrawstwem z zainteresowaniem, więc moje pytanie brzmi: jak wyglądałyby testy jednostkowe, które zawiodłyby po wprowadzeniu alfanumerycznych kodów branżowych?

Matt Evans
źródło
17
Wygląda na to, że przeoczyli również test integracyjny, który sprawdził liczbę transakcji wyeksportowanych do SEC. Jeśli zbudujesz funkcję eksportu, będzie to rozsądna kontrola.
Luc Franken,
31
Autor artykułu nie rozumie testów jednostkowych. Niektóre twierdzenia są po prostu absurdalne ( „jest mało prawdopodobne, aby testy jednostkowe przetestowały więcej niż jedną bilionową funkcjonalność jakiejkolwiek metody” ), inne zniszczyłyby szansę na regresję ( „spójrz na testy, które nigdy nie zawiodły w ciągu roku i rozważ wyrzucenie ich ” ). Lub sugestie, takie jak „zamień testy jednostkowe w asercje” , które mają zmienić nieudane testy wyjątków czasu wykonywania?
Groo,
25
@gnat Nie przeczytałem zewnętrznego linku i nadal uważałem to pytanie za istotne
Jeutnarg
23
Co do tego, co jest warte, prawie nie zgadzam się ze wszystkim w „Dlaczego większość testów jednostkowych to odpady”. Napisałbym obalenie, ale ten margines jest zbyt mały, by go pomieścić.
Robert Harvey

Odpowiedzi:

19

Czy naprawdę pytasz: „czy pomogłyby tutaj testy jednostkowe?”, Czy pytasz: „czy pomógłby w tym jakikolwiek test?”.

Najbardziej oczywistą formą testowania, która by pomogła, jest wstępne stwierdzenie w samym kodzie, że identyfikator gałęzi składa się tylko z cyfr (zakładając, że jest to założenie, na którym koder opierał się podczas pisania kodu).

Mogło to nie powieść się w jakimś teście integracji, a gdy tylko zostaną wprowadzone nowe identyfikatory gałęzi alfanumerycznej, asercja wysadzi w powietrze. Ale to nie jest test jednostkowy.

Alternatywnie może istnieć test integracji procedury, która generuje raport SEC. Ten test zapewnia, że ​​każdy rzeczywisty identyfikator oddziału zgłasza swoje transakcje (i dlatego wymaga rzeczywistych danych wejściowych, listy wszystkich używanych identyfikatorów oddziału). Więc to też nie jest test jednostkowy.

Nie widzę żadnej definicji ani dokumentacji zaangażowanych interfejsów, ale może się zdarzyć, że testy jednostkowe prawdopodobnie nie wykryły błędu, ponieważ jednostka nie była wadliwa . Jeśli jednostka może założyć, że identyfikatory gałęzi składają się tylko z cyfr, a programiści nigdy nie zdecydowali, co powinien zrobić kod, gdyby tego nie zrobił, to nie powinninapisz test jednostkowy, aby wymusić określone zachowanie w przypadku niecyfrowych identyfikatorów, ponieważ test odrzuciłby hipotetyczną poprawną implementację jednostki, która poprawnie obsługiwała alfanumeryczne identyfikatory gałęzi, i zwykle nie chcesz pisać testu jednostkowego, który uniemożliwia prawidłowy przyszłe wdrożenia i rozszerzenia. A może jeden dokument napisany 40 lat temu domyślnie zdefiniowany (poprzez pewien zakres leksykograficzny w surowym EBCDIC, zamiast bardziej przyjaznej dla człowieka reguły zestawiania), że 10B jest identyfikatorem testu, ponieważ w rzeczywistości mieści się w przedziale od 089 do 100. Ale potem 15 lat temu ktoś postanowił użyć go jako prawdziwego identyfikatora, więc „usterka” nie leży w jednostce, która poprawnie implementuje oryginalną definicję: leży w procesie, który nie zauważył, że 10B jest zdefiniowany jako identyfikator testu i dlatego nie powinien być przypisany do gałęzi. To samo stanie się w ASCII, jeśli zdefiniujesz 089 - 100 jako zakres testowy, a następnie wprowadzisz identyfikator 10 $ lub 1.0. Po prostu zdarza się, że w EBCDIC cyfry pojawiają się po literach.

Jeden test jednostkowy (lub prawdopodobnie test funkcjonalny), który jest możliwymógł uratować dzień, jest testem jednostki, która generuje lub sprawdza nowe identyfikatory gałęzi. Test ten potwierdziłby, że identyfikatory muszą zawierać tylko cyfry i zostałby napisany, aby umożliwić użytkownikom identyfikatorów gałęzi przyjęcie tego samego. A może jest gdzieś jednostka, która importuje prawdziwe identyfikatory gałęzi, ale nigdy nie widzi tych testowych, i która mogłaby być testowana jednostkowo, aby upewnić się, że odrzuca wszystkie identyfikatory testowe (jeśli identyfikatory to tylko trzy znaki, możemy je wszystkie policzyć i porównać zachowanie walidator do filtru testowego, aby upewnić się, że pasują, co dotyczy zwykłego sprzeciwu wobec testów punktowych). Gdy ktoś zmienił reguły, test jednostkowy nie powiódłby się, ponieważ jest sprzeczny z nowo wymaganym zachowaniem.

Ponieważ test został przeprowadzony z dobrego powodu, punkt, w którym należy go usunąć ze względu na zmienione wymagania biznesowe, staje się okazją dla kogoś, kto dostanie to zadanie, „znajdź każde miejsce w kodzie, które zależy od zachowania, które chcemy zmiana". Oczywiście jest to trudne, a zatem niewiarygodne, więc w żadnym wypadku nie gwarantuje uratowania dnia. Ale jeśli uchwycisz swoje założenia w testach jednostek, których zakładasz właściwości, to dałeś sobie szansę, więc wysiłek nie zostanie całkowicie zmarnowany.

Zgadzam się oczywiście, że gdyby jednostka nie została zdefiniowana z „zabawnym” wejściem, nie byłoby nic do przetestowania. Fiddly podziały przestrzeni nazw mogą być trudne do prawidłowego przetestowania, ponieważ trudność nie polega na implementacji twojej śmiesznej definicji, polega na upewnieniu się, że wszyscy rozumieją i szanują twoją zabawną definicję. To nie jest lokalna właściwość jednej jednostki kodu. Ponadto zmiana niektórych typów danych z „ciągu cyfr” na „ciąg znaków alfanumerycznych” przypomina tworzenie Unicode w programie opartym na ASCII: nie będzie to łatwe, jeśli kod będzie silnie sprzężony z oryginalną definicją i kiedy typ danych ma fundamentalne znaczenie dla tego, co robi program, a następnie często jest silnie sprzężony.

myślenie, że to w dużej mierze zmarnowany wysiłek, jest trochę niepokojące

Jeśli testy jednostkowe czasami kończą się niepowodzeniem (na przykład podczas refaktoryzacji), a robiąc to, dostarczają przydatnych informacji (na przykład Twoja zmiana jest błędna), to wysiłek nie został zmarnowany. To, czego nie robią, to sprawdzenie, czy system działa. Więc jeśli piszesz testy jednostkowe zamiast testów funkcjonalnych i integracyjnych, być może wykorzystujesz swój czas nieoptymalnie.

Steve Jessop
źródło
Asercje są dobre!
3
@nocomprende: jak Reagan, „ufaj, ale weryfikuj”.
Steve Jessop,
1
Chciałem też powiedzieć „Testy jednostkowe źle!” ale myślałem, że większość ludzi nie zauważy odniesienia do Farmy Zwierząt i zacznę krytykować mnie, zamiast myśleć o tym, co mówię (reakcje kolana są nieskuteczne), ale tego nie powiedziałem. Być może osoba, która jest mądrzejsza i ma większą erudycję, może to zrobić.
2
„Wszystkie testy przechodzą pomyślnie, ale niektóre z nich przechodzą WIĘCEJ niż inne!”
Graham,
1
badanie to czerwony śledź. Ci faceci po prostu nie wiedzieli, jak zdefiniowano „kod oddziału”. To tak, jakby poczta amerykańska nie wiedziała, że ​​zmienia definicję kodu pocztowego, dodając 4 cyfry.
radarbob
120

Testy jednostkowe mogły wykryć, że kody gałęzi 10B i 10C zostały nieprawidłowo sklasyfikowane jako „gałęzie testujące”, ale wydaje mi się mało prawdopodobne, aby testy dla tej klasyfikacji gałęzi były wystarczająco obszerne, aby wykryć ten błąd.

Z drugiej strony kontrole na miejscu wygenerowanych raportów mogły ujawnić, że rozgałęzionych 10B i 10C konsekwentnie brakowało w raportach znacznie wcześniej niż 15 lat, kiedy błąd mógł pozostać obecny.

Wreszcie jest to dobra ilustracja, dlaczego mieszanie danych testowych z rzeczywistymi danymi produkcyjnymi w jednej bazie danych jest złym pomysłem. Gdyby korzystali z osobnej bazy danych zawierającej dane testowe, nie byłoby potrzeby odfiltrowywania ich z oficjalnych raportów i niemożliwe byłoby odfiltrowanie zbyt dużej ilości danych.

Bart van Ingen Schenau
źródło
80
+1 Testy jednostkowe nigdy nie mogą zrekompensować złych decyzji projektowych (takich jak test mieszania i rzeczywiste dane)
Jeutnarg 14.07.16
5
Chociaż najlepiej unikać mieszania danych testowych z danymi rzeczywistymi, sprawdzenie poprawności systemu produkcyjnego może być trudne, jeśli wymaga to modyfikacji danych rzeczywistych. Na przykład złym pomysłem jest sprawdzanie poprawności systemu bankowego poprzez modyfikowanie sum rachunków bankowych w produkcji. Użycie zakresów kodu do oznaczenia znaczenia jest problematyczne. Bardziej wyraźny atrybut zapisów byłby prawdopodobnie lepszym wyborem.
JimmyJames
4
@Voo Myślę, że istnieje milczące założenie, że istnieje poziom złożoności lub wymaganie niezawodności, w których testowanie rzeczywistego, wdrożonego systemu produkcyjnego jest uważane za opłacalne lub konieczne. (Zastanów się, ile może pójść nie tak z powodu złej zmiennej konfiguracyjnej.) Widziałem, że dotyczy to dużej instytucji finansowej.
jpmc26
4
@Voo Nie mówię o testowaniu. Mówię o walidacji systemu. W prawdziwym systemie produkcyjnym może się nie powieść na wiele sposobów, które nie mają nic wspólnego z kodem. Jeśli wprowadzasz nowy system bankowy do produkcji, możesz mieć jakiś problem w bazie danych lub sieci itp., Który uniemożliwia zastosowanie transakcji do kont. Nigdy nie pracowałem w banku, ale jestem prawie pewien, że marszczy go to, gdy zaczyna modyfikować rzeczywiste konta za pomocą fałszywych transakcji. Dzięki temu możesz założyć fałszywe konta lub poczekać i pomodlić się.
JimmyJames
12
@JimmyJames W służbie zdrowia powszechne jest okresowe kopiowanie produkcyjnej bazy danych do środowiska testowego w celu przeprowadzenia testów na danych, które są jak najbardziej zbliżone do rzeczywistych; Myślę, że bank może zrobić to samo.
dj18
75

Oprogramowanie musiało obsługiwać pewne reguły biznesowe. Gdyby były testy jednostkowe, testy jednostkowe sprawdziłyby, czy oprogramowanie poprawnie obsługuje reguły biznesowe.

Reguły biznesowe uległy zmianie.

Najwyraźniej nikt nie zdawał sobie sprawy, że reguły biznesowe uległy zmianie i nikt nie zmienił oprogramowania, aby zastosować nowe reguły biznesowe. Gdyby były testy jednostkowe, te testy jednostkowe musiałyby zostać zmienione, ale nikt by tego nie zrobił, ponieważ nikt nie zdawał sobie sprawy, że zmieniły się reguły biznesowe.

Więc nie, testy jednostkowe by tego nie złapały.

Wyjątkiem byłoby, gdyby testy jednostkowe i oprogramowanie zostały utworzone przez niezależne zespoły, a zespół wykonujący testy jednostkowe zmienił testy, aby zastosować nowe reguły biznesowe. Wówczas testy jednostkowe zakończyłyby się niepowodzeniem, co, miejmy nadzieję, spowodowałoby zmianę oprogramowania.

Oczywiście w tym samym przypadku, gdyby zmieniono tylko oprogramowanie, a nie testy jednostkowe, testy jednostkowe również się nie udały. Ilekroć test jednostkowy kończy się niepowodzeniem, nie oznacza to, że oprogramowanie jest złe, oznacza to, że oprogramowanie lub test jednostkowy (czasami oba) są nieprawidłowe.

gnasher729
źródło
2
Czy jest możliwe posiadanie różnych zespołów, w których jeden pracuje nad kodem, a drugi nad testami „jednostkowymi”? Jak to w ogóle możliwe? ... Cały czas refaktoryzuję kod.
Sergio
2
@Sergio z jednej perspektywy, refaktoryzacja zmienia elementy wewnętrzne, zachowując zachowanie - więc jeśli test jest napisany w sposób, który testuje zachowanie bez polegania na elementach wewnętrznych, to nie wymaga aktualizacji.
Daenyth,
1
Widziałem to wiele razy. Oprogramowanie jest w produkcji bez żadnych reklam, a następnie nagle użytkownicy eksplodują z reklamacjami, że przestało ono działać i stopniowo zanikało przez lata. Tak się dzieje, gdy decydujesz się na zmianę procedur wewnętrznych bez przeprowadzania standardowego procesu powiadamiania ...
Brian Knoblauch,
42
„Zmiana reguł biznesowych” jest krytyczną obserwacją. Testy jednostkowe potwierdzają, że zaimplementowałeś logikę, którą Twoim zdaniem zaimplementowałeś , a nie, że logika była poprawna .
Ryan Cavanaugh
5
Jeśli mam rację co do tego, co się stało, jest mało prawdopodobne, aby testy jednostkowe, które to wykryły, zostały zapisane. Podstawową zasadą wyboru testów jest testowanie niektórych „dobrych” przypadków, niektórych „złych” przypadków i przypadków obejmujących dowolne granice. W takim przypadku przetestowałbyś „099”, „100” i „101”. Ponieważ „10B” był objęty testami „odrzucania nieliczbowych” w starym systemie i jest większy niż 101 (i tak też obejmuje to testowanie) w nowym systemie, nie ma powodu, aby go testować - poza tym, że w EBCDIC, „10B” sortuje między „099” a „100”.
Mark
29

Nie. To jeden z dużych problemów z testowaniem jednostkowym: wprawiają cię w fałszywe poczucie bezpieczeństwa.

Jeśli wszystkie testy przejdą pomyślnie, nie oznacza to, że system działa poprawnie; oznacza to, że wszystkie twoje testy przeszły pomyślnie . Oznacza to, że części twojego projektu, o których świadomie myślałeś i napisałeś testy, działają tak, jak ci się wydaje, co naprawdę nie jest aż tak wielkim problemem: na te rzeczy zwracałeś szczególną uwagę , więc jest bardzo prawdopodobne, że i tak masz rację! Ale nie robi nic, aby złapać przypadki, o których nigdy nie pomyślałeś, takie jak ten, ponieważ nigdy nie pomyślałeś o napisaniu dla nich testu. (Gdyby tak było, byłbyś świadomy, że to oznaczało konieczność zmiany kodu i musiałbyś je zmienić).

Mason Wheeler
źródło
17
Mój ojciec pytał mnie kiedyś: dlaczego nie pomyślałeś o rzeczach, o których nie myślałeś? (Tylko on sprawiał, że było to mylące, mówiąc „Jeśli nie wiesz, zapytaj !”) Ale skąd mam wiedzieć, że nie wiem?
7
„Oznacza to, że części twojego projektu, o których świadomie pomyślałeś i napisałeś testy, działają tak, jak ci się wydaje.” Dokładnie tak. Informacje te są nieocenione, jeśli dokonujesz refaktoryzacji lub jeśli coś zmieni się gdzieś indziej w systemie, co złamie twoje założenia. Programiści uśpieni fałszywym poczuciem bezpieczeństwa po prostu nie rozumieją ograniczeń testów jednostkowych, ale to nie czyni z testowania jednostkowego bezużytecznego narzędzia.
Robert Harvey
12
@MasonWheeler: Podobnie jak ty, autor uważa, że ​​testy jednostkowe mają jakoś udowodnić, że twój program działa. Nie ma Powtórzę: testowanie jednostkowe nie dowodzi, że twój program działa. Testy jednostkowe dowodzą, że Twoje metody spełniają warunki umowy testowej i to wszystko. Reszta papieru upada, ponieważ opiera się na tej jednej nieprawidłowej przesłance.
Robert Harvey
5
Oczywiście programiści, którzy mają takie fałszywe przekonanie, będą rozczarowani, gdy testy jednostkowe całkowicie ich zawiodą, ale to wina programisty, a nie testów jednostkowych i nie unieważnia to prawdziwej wartości, jaką zapewnia testowanie jednostkowe.
Robert Harvey
5
o_O @ twoje pierwsze zdanie. Testy jednostkowe dają fałszywe poczucie bezpieczeństwa, a kodowanie, takie jak trzymanie rąk na kierownicy, daje fałszywe poczucie bezpieczeństwa podczas jazdy.
djechlin
10

Nie, niekoniecznie.

Pierwotnym wymogiem było użycie numerycznych kodów branżowych, dlatego zostałby przeprowadzony test jednostkowy dla komponentu, który akceptuje różne kody i odrzuca jakikolwiek 10B. System zostałby uznany za działający (który był).

Następnie wymaganie zmieniłoby się, a kody zaktualizowane, ale oznaczałoby to, że kod testu jednostkowego, który dostarczył złe dane (czyli teraz dobre dane) musiałby zostać zmodyfikowany.

Teraz zakładamy, że ludzie zarządzający systemem wiedzieliby, że tak jest, i zmieniliby test jednostkowy, aby obsługiwać nowe kody ... ale gdyby wiedzieli, że tak się dzieje, mogliby również zmienić kod, który obsługiwał te kody i tak kody… i tego nie zrobili. Test jednostkowy, który pierwotnie odrzucił kod 10B, z radością powiedziałby „wszystko jest w porządku” po uruchomieniu, gdybyś nie wiedział, jak zaktualizować ten test.

Testy jednostkowe są dobre do oryginalnego opracowania, ale nie do testowania systemu, zwłaszcza nie 15 lat po dawnym zapomnieniu o wymaganiach.

W takiej sytuacji potrzebują kompleksowego testu integracji. Taki, w którym możesz przekazać dane, które mają działać, i sprawdzić, czy to działa. Ktoś zauważyłby, że ich nowe dane wejściowe nie wygenerowały raportu, a następnie przeprowadziłby dalsze dochodzenie.

gbjbaanb
źródło
Spot on. I główny (tylko?) Problem z testami jednostkowymi. Ocaliłem mnie, formułując własną odpowiedź, ponieważ powiedziałbym dokładnie to samo (ale prawdopodobnie jeszcze gorzej!) :)
Lekkość ściga się na orbicie
8

Testowanie typu (proces testowania niezmienników przy użyciu losowo generowanych prawidłowych danych, na przykład w bibliotece testowej Haskell QuickCheck i różnych portach / alternatywach zainspirowanych przez nią w innych językach) może wychwycić ten problem, testowanie jednostkowe prawie na pewno by się nie udało .

Wynika to z faktu, że kiedy zasady ważności kodów oddziałów zostały zaktualizowane, jest mało prawdopodobne, aby ktokolwiek pomyślał o przetestowaniu tych konkretnych zakresów, aby upewnić się, że działają one poprawnie.

Jeśli jednak testowanie typu było w użyciu, ktoś powinien w momencie wdrożenia oryginalnego systemu napisać parę właściwości, jedną dla sprawdzenia, czy określone kody dla gałęzi testowych zostały potraktowane jako dane testowe, a drugą dla sprawdzenia, czy nie ma innych kodów były ... kiedy definicja typu danych dla kodu oddziału została zaktualizowana (co byłoby wymagane w celu umożliwienia testowania, czy którakolwiek ze zmian kodu oddziału z cyfrowego na numeryczny działała), test ten rozpocząłby testowanie wartości w nowy zakres i najprawdopodobniej zidentyfikowałby usterkę.

Oczywiście QuickCheck został opracowany po raz pierwszy w 1999 roku, więc było już za późno, aby złapać ten problem.

Jules
źródło
1
Sądzę, że bardziej normalne jest nazywanie tego testu opartego na właściwościach i oczywiście jest tak dobrze, jak to możliwe napisanie testu opartego na właściwościach, który nadal przejdzie po tej zmianie (chociaż myślę, że bardziej prawdopodobne jest napisanie testu, który mógłby go znaleźć)
jk.
5

Naprawdę wątpię, by testy jednostkowe miały wpływ na ten problem. To brzmi jak jedna z sytuacji tunelowych, ponieważ zmieniono funkcjonalność w celu obsługi nowych kodów oddziałów, ale nie zostało to przeprowadzone we wszystkich obszarach systemu.

Używamy testów jednostkowych, aby zaprojektować klasę. Ponowne uruchomienie testu jednostkowego jest wymagane tylko w przypadku zmiany projektu. Jeśli konkretna jednostka nie ulegnie zmianie, wówczas niezmienione testy jednostkowe zwrócą takie same wyniki jak poprzednio. Testy jednostkowe nie pokażą ci wpływu zmian na inne jednostki (jeśli nie, nie piszesz testów jednostkowych).

Możesz rozsądnie wykryć ten problem tylko poprzez:

  • Testy integracyjne - ale trzeba by specjalnie dodać nowe formaty kodu, aby przejść przez wiele jednostek w systemie (tzn. Pokazałyby one problem tylko wtedy, gdy oryginalne testy obejmowały obecnie obowiązujące gałęzie)
  • Kompleksowe testy - firma powinna przeprowadzić kompleksowy test obejmujący stare i nowe formaty kodów branżowych

Bardziej niepokojące jest brak wystarczających kompleksowych testów. Nie można polegać na testach jednostkowych jako teście TYLKO lub GŁÓWNY w przypadku zmian w systemie. Wygląda na to, że wymagało to tylko sporządzenia raportu o nowo obsługiwanych formatach kodu oddziału.

Szkielet klasy
źródło
2

Zapewnienie wbudowane w środowisko wykonawcze mogło pomóc; na przykład:

  1. Utwórz funkcję podobną do bool isTestOnly(string branchCode) { ... }
  2. Użyj tej funkcji, aby zdecydować, które raporty mają zostać odfiltrowane
  3. Ponownie użyj tej funkcji w stwierdzeniu, w kodzie tworzenia gałęzi, aby zweryfikować lub stwierdzić, że gałąź nie jest (nie może być) utworzona przy użyciu tego typu kodu gałęzi‼
  4. Włącz to twierdzenie w czasie rzeczywistym (a nie „zoptymalizuj, z wyjątkiem wersji deweloperskiej kodu tylko do debugowania”)‼

Zobacz też:

ChrisW
źródło
2

Wyzwanie polega na tym, by szybko zawieść .

Nie mamy kodu ani nie mamy wielu przykładów prefiksów, które są lub nie są prefiksami gałęzi testowych zgodnie z kodem. Wszystko, co mamy, to:

  • 089 - 100 => gałąź testowa
  • 10B, 10C => gałąź testowa
  • <088 => przypuszczalnie prawdziwe gałęzie
  • > 100 => przypuszczalnie prawdziwe gałęzie

Fakt, że kod dopuszcza liczby i ciągi, jest więcej niż trochę dziwny. Oczywiście 10B i 10C można uznać za liczby szesnastkowe, ale jeśli wszystkie prefiksy są traktowane jako liczby szesnastkowe, 10B i 10C nie mieszczą się w zakresie testowym i będą traktowane jako rzeczywiste rozgałęzienia.

Prawdopodobnie oznacza to, że prefiks jest przechowywany jako ciąg, ale w niektórych przypadkach jest traktowany jako liczba. Oto najprostszy kod, jaki mogę wymyślić, który replikuje to zachowanie (używając C # w celach ilustracyjnych):

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

W języku angielskim, jeśli ciąg jest liczbą i zawiera się między 89 a 100, jest to test. Jeśli to nie jest liczba, to jest test. W przeciwnym razie nie jest to test.

Jeśli kod jest zgodny z tym wzorcem, żaden test jednostkowy nie wychwyciłby tego w momencie wdrażania kodu. Oto kilka przykładowych testów jednostkowych:

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

Test jednostkowy pokazuje, że „10B” należy traktować jako gałąź testową. Użytkownik @ gnasher729 powyżej mówi, że reguły biznesowe uległy zmianie i właśnie to pokazuje ostatnie stwierdzenie powyżej. W pewnym momencie to twierdzenie powinno się przełączyć na isFalse, ale tak się nie stało. Testy jednostkowe uruchamiane są w fazie projektowania i kompilacji, ale potem nie są wykonywane.


Jaka jest lekcja tutaj? Kod musi w jakiś sposób zasygnalizować, że otrzymał nieoczekiwany sygnał wejściowy. Oto alternatywny sposób napisania tego kodu, który podkreśla, że ​​oczekuje on, że prefiks będzie liczbą:

// 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;
}

Dla tych, którzy nie znają C #, zwracana wartość wskazuje, czy kod był w stanie przeanalizować prefiks z podanego ciągu. Jeśli zwracana wartość jest prawdą, kod wywołujący może użyć zmiennej isTest out, aby sprawdzić, czy prefiks gałęzi jest prefiksem testowym. Jeśli zwracana wartość to false, kod wywołujący powinien zgłosić, że podany prefiks nie jest oczekiwany, a zmienna isTest out nie ma znaczenia i należy ją zignorować.

Jeśli nie masz nic przeciwko wyjątkom, możesz to zrobić w zamian:

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

Ta alternatywa jest prostsza. W takim przypadku kod wywołujący musi wychwycić wyjątek. W obu przypadkach kod powinien mieć jakiś sposób zgłaszania dzwoniącemu, że nie spodziewał się strPrefiksu, którego nie można przekonwertować na liczbę całkowitą. W ten sposób kod szybko zawiedzie, a bank może szybko znaleźć problem bez drobnego zażenowania SEC.

user2023861
źródło
1

Tak wiele odpowiedzi i nawet nie jeden cytat Dijkstry:

Testy pokazują obecność, a nie brak błędów.

Dlatego to zależy. Jeśli kod został przetestowany poprawnie, najprawdopodobniej ten błąd nie istniałby.

BЈовић
źródło
-1

Myślę, że tutaj test jednostkowy zapewniłby, że problem nigdy nie wystąpi.

Zastanów się, napisałeś bool IsTestData(string branchCode)funkcję.

Pierwszy test jednostkowy, który napiszesz, powinien być pusty i pusty. Następnie dla łańcuchów o niepoprawnej długości, a następnie dla łańcuchów niecałkowitych.

Aby wszystkie testy zakończyły się pomyślnie, należy dodać funkcję sprawdzania parametrów do funkcji.

Nawet jeśli wtedy testujesz tylko dla „dobrych” danych 001 -> 999, nie myśląc o możliwości 10A, sprawdzanie parametrów zmusi cię do przepisania funkcji, gdy zaczniesz używać alfanumerycznych, aby uniknąć wyjątków, które wyrzuci

Ewan
źródło
1
To by nie pomogło - funkcja nie została zmieniona, a test nie zacząłby się nie powieść, biorąc pod uwagę te same dane testowe. Ktoś musiałby pomyśleć o zmianie testu, aby nie powiódł się, ale gdyby pomyślał o tym, prawdopodobnie pomyślałby również o zmianie funkcji.
Hulk
(A może coś mi brakuje, ponieważ nie jestem pewien, co masz na myśli przez „sprawdzanie parametrów”)
Hulk
Funkcja byłaby zmuszona zgłosić wyjątek dla ciągów niecałkowitych, aby przejść prosty test jednostkowy przypadku krawędzi. Dlatego kod produkcyjny popełniłby błąd, gdybyś zaczął używać alfanumerycznych kodów gałęzi bez specjalnego programowania
Ewan
Ale czy funkcja nie IsValidBranchCodeużyłaby jakiejś funkcji do wykonania tej kontroli? A ta funkcja prawdopodobnie zostałaby zmieniona bez potrzeby modyfikacji IsTestData? Więc jeśli testujesz tylko „dobre dane”, test nie pomógłby. Test przypadków skrajnych musiałby zawierać teraz aktualny kod oddziału (a nie tylko niektóre nadal niepoprawne), aby zacząć się nie udać.
Hulk
1
Jeśli kontrola jest w IsValidCode, więc funkcja przechodzi bez własnego jawnego sprawdzenia, to tak, można ją przeoczyć, ale wtedy mielibyśmy dodatkowy zestaw jeszcze większej liczby testów, próbnych walidatorów itp. Jeszcze większe szanse na konkretne „są to numery testowe ”
Ewan