Testy mają na celu wsparcie i zapewnienie programowania obronnego
Programowanie defensywne chroni integralność systemu w czasie wykonywania.
Testy to (głównie statyczne) narzędzia diagnostyczne. Nigdzie nie widać twoich testów. Są jak rusztowania używane do wznoszenia wysokiego ceglanego muru lub kamiennej kopuły. Nie pozostawiasz ważnych elementów poza konstrukcją, ponieważ masz rusztowanie, które utrzymuje je podczas budowy. Masz rusztowanie podtrzymujące go podczas budowy do ułatwić umieszczenie wszystkich ważnych elementów.
EDYCJA: analogia
Co z analogią do komentarzy w kodzie?
Komentarze mają swój cel, ale mogą być zbędne, a nawet szkodliwe. Na przykład, jeśli w komentarzach umieścisz wewnętrzną wiedzę na temat kodu , a następnie zmienisz kod, komentarze w najlepszym wypadku będą nieistotne, aw najgorszym szkodliwe.
Powiedzmy, że w testach włożyłeś dużo wewnętrznej wiedzy o podstawie kodu, na przykład Metoda A nie może przyjmować wartości zerowej i argumentem MethodB musi być > 0
. Następnie kod się zmienia. Null jest teraz w porządku dla A, a B może przyjmować wartości tak małe jak -10. Istniejące testy są teraz funkcjonalnie niepoprawne, ale nadal będą zaliczane.
Tak, powinieneś aktualizować testy w tym samym czasie, gdy aktualizujesz kod. Powinieneś także aktualizować (lub usuwać) komentarze w tym samym czasie, gdy aktualizujesz kod. Ale wszyscy wiemy, że te rzeczy nie zawsze się zdarzają i że popełniane są błędy.
Testy weryfikują zachowanie systemu. Że rzeczywiste zachowanie jest nieodłączną częścią samego systemu, a nie dla testów.
Co może pójść nie tak?
Celem w odniesieniu do testów jest wymyślenie wszystkiego, co może pójść nie tak, napisanie testu sprawdzającego prawidłowe zachowanie, a następnie opracowanie kodu środowiska wykonawczego, aby przejść wszystkie testy.
Co oznacza, że chodzi o programowanie obronne .
Napędy TDD programowanie obronne, jeśli testy są kompleksowe.
Więcej testów, prowadzenie bardziej defensywnego programowania
Kiedy nieuchronnie zostaną znalezione błędy, pisanych jest więcej testów w celu modelowania warunków, w których występuje błąd. Następnie kod jest stała, z kodem do dokonania tych testy przechodzą, a nowe badania pozostają w zestawu testowego.
Dobry zestaw testów przekazuje zarówno dobre, jak i złe argumenty do funkcji / metody i oczekuje spójnych wyników. To z kolei oznacza, że testowany komponent użyje kontroli warunków wstępnych (programowanie obronne), aby potwierdzić przekazane mu argumenty.
Ogólnie mówiąc ...
Na przykład, jeśli argument zerowy dla określonej procedury jest niepoprawny, to co najmniej jeden test przejdzie zero i będzie oczekiwać pewnego rodzaju wyjątku / błędu „nieprawidłowy argument zerowy”.
Co najmniej jeden inny test zamierza przekazać ważne oczywiście argument - lub zapętli dużą tablicę i przekaże wiele poprawnych argumentów - i potwierdzi, że wynikowy stan jest odpowiedni.
Jeśli test nie przejdzie tego argumentu zerowego i zostanie spoliczkowany oczekiwanym wyjątkiem (a ten wyjątek został zgłoszony, ponieważ kod obronnie sprawdził przekazany mu stan), wówczas wartość NULL może zostać przypisana do właściwości klasy lub zakopana w jakiejś kolekcji, w której nie powinno być.
Może to spowodować nieoczekiwane zachowanie w zupełnie innej części systemu, do której przekazywana jest instancja klasy, w odległych lokalizacjach geograficznych po dostarczeniu oprogramowania . I tego właśnie staramy się uniknąć, prawda?
Może być nawet gorzej. Instancja klasy ze stanem niepoprawnym może zostać zserializowana i zapisana w pamięci, tylko w celu spowodowania awarii, gdy zostanie odtworzona w celu późniejszego wykorzystania. Rany, nie wiem, może to jakiś mechaniczny system sterowania, który nie może się zrestartować po wyłączeniu, ponieważ nie może deserializować własnego stanu konfiguracji trwałej. Lub instancja klasy może zostać przekształcona do postaci szeregowej i przekazana do zupełnie innego systemu utworzonego przez inną jednostkę, a ten system może ulec awarii.
Zwłaszcza jeśli programiści tego innego systemu nie kodowali defensywnie.