Pisanie testów dla istniejącego kodu

68

Załóżmy, że jeden miał stosunkowo duży program (powiedzmy 900k SLOC w C #), wszystkie skomentowane / udokumentowane dokładnie, dobrze zorganizowane i działające dobrze. Cała baza kodu została napisana przez jednego starszego programistę, który nie współpracuje już z firmą. Cały kod jest testowalny w obecnej postaci, a IoC jest używany przez cały czas - z jakiegoś dziwnego powodu nie napisali żadnych testów jednostkowych. Teraz Twoja firma chce rozgałęzić kod i chce dodać testy jednostkowe, aby wykryć, kiedy zmiany psują podstawową funkcjonalność.

  • Czy dodawanie testów to dobry pomysł?
  • Jeśli tak, to jak zacząć od czegoś takiego?

EDYTOWAĆ

OK, więc nie spodziewałem się, że odpowiedzi będą dobrym argumentem na przeciwne wnioski. W każdym razie problem może być poza moim zasięgiem. Przeczytałem również „duplikaty pytań” i ogólny konsensus jest taki, że „testy pisemne są dobre” ... tak, ale nie są zbyt pomocne w tym konkretnym przypadku.

Nie sądzę, że jestem tu sam, rozważając pisanie testów starszego systemu. Zamierzam zachować wskaźniki dotyczące tego, ile czasu spędza się i ile razy nowe testy wychwytują problemy (i ile razy nie). Wrócę i zaktualizuję to za około rok z moimi wynikami.

WNIOSEK

Okazuje się zatem, że w zasadzie niemożliwe jest po prostu dodanie testu jednostkowego do istniejącego kodu z jakimkolwiek pozorem ortodoksji. Gdy kod działa, oczywiście nie można na czerwono / zielono naświetlić swoich testów, zwykle nie jest jasne, które zachowania są ważne do przetestowania, nie jest jasne od czego zacząć i na pewno nie jest jasne, kiedy skończysz. Naprawdę nawet postawienie tego pytania pomija główny punkt pisania testów. W większości przypadków stwierdziłem, że łatwiej jest ponownie napisać kod przy użyciu TDD niż rozszyfrować zamierzone funkcje i dodać z mocą wsteczną testy jednostkowe. Naprawianie problemu lub dodawanie nowej funkcji to inna historia i uważam, że nadszedł czas na dodanie testów jednostkowych (jak niektórzy podkreślili poniżej). W końcu większość kodu zostaje przepisana, często wcześniej niż można by się spodziewać - przy takim podejściu „

Paweł
źródło
10
Dodanie testów nie zepsuje istniejącego kodu.
Dan Pichelman
30
@ DanPichelman Nigdy nie spotkałeś schroedinbuga - „Błąd w projekcie lub implementacji w programie, który nie pojawia się, dopóki ktoś czytający źródło lub korzystający z programu w nietypowy sposób zauważy, że nigdy nie powinien działać, w którym to momencie program natychmiast przestaje działać dla wszystkich, dopóki nie zostanie naprawiony ”.
8
@MichaelT Teraz, kiedy o tym wspominasz, myślę, że widziałem jeden lub dwa z nich. Mój komentarz powinien brzmieć „Dodanie testów zwykle nie psuje istniejącego kodu”. Dzięki
Dan Pichelman,
3
Testy pisz tylko wokół rzeczy, które zamierzasz refaktoryzować lub zmienić.
Steven Evers,
3
Sprawdź książkę „Skuteczna praca ze starszym kodem ”: amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/... , Michael Feathers sugeruje napisanie testów przed zmodyfikowaniem dowolnego starszego kodu.
Skarab

Odpowiedzi:

68

Chociaż testy są dobrym pomysłem, intencją pierwotnego kodera było ich zbudowanie, ponieważ budował aplikację, aby przechwycić swoją wiedzę na temat tego, jak powinien działać kod i co może się zepsuć, co zostanie następnie przekazane tobie.

Przyjmując to podejście, istnieje duże prawdopodobieństwo, że napiszesz testy, które najprawdopodobniej się nie zepsują, i przeoczysz większość przypadków skrajnych, które zostałyby odkryte podczas tworzenia aplikacji.

Problem polega na tym, że większość wartości będzie pochodzić z tych „wpadek” i mniej oczywistych sytuacji. Bez tych testów zestaw testów traci praktycznie całą swoją skuteczność. Ponadto firma będzie miała fałszywe poczucie bezpieczeństwa w związku ze swoją aplikacją, ponieważ nie będzie ona znacznie bardziej odporna na regresję.

Zazwyczaj sposobem obsługi tego typu bazy kodów jest pisanie testów dla nowego kodu i refaktoryzacja starego kodu, dopóki starsza baza kodu nie zostanie całkowicie zrefaktoryzowana.

Zobacz także .

FMJaguar
źródło
11
Ale nawet bez TDD testy jednostkowe są przydatne podczas refaktoryzacji.
pdr
1
Jeśli kod nadal działa dobrze, nie stanowi to problemu, ale najlepiej jest przetestować interfejs do starszego kodu za każdym razem, gdy napiszesz cokolwiek, co zależy od jego zachowania.
deworde
1
Dlatego mierzysz zasięg testu . Jeśli test dla konkretnej sekcji nie obejmuje wszystkich ifs i els oraz wszystkich przypadków brzegowych, nie można bezpiecznie refaktoryzować tej sekcji. Pokrycie poinformuje cię, czy wszystkie linie zostaną trafione, więc Twoim celem jest jak największe zwiększenie zasięgu przed refaktoryzacją.
Rudolf Olah,
3
Główną wadą TDD jest to, że chociaż pakiet może działać, dla programisty nieznajomego podstawy kodu jest to fałszywe poczucie bezpieczeństwa. BDD jest pod tym względem znacznie lepszy, ponieważ wynik jest intencją kodu w prostym języku angielskim.
Robbie Dee,
3
Wystarczy wspomnieć, że 100% pokrycia kodu nie oznacza, że ​​Twój kod działa poprawnie przez 100% czasu. Możesz przetestować każdy wiersz kodu dla metody, ale to, że działa z wartością 1, nie oznacza, że ​​z pewnością będzie działać z wartością 2.
ryanzec
35

Tak, dodanie testów jest zdecydowanie dobrym pomysłem.

Mówisz, że jest dobrze udokumentowany, a to daje ci dobrą pozycję. Spróbuj utworzyć testy, korzystając z tej dokumentacji jako przewodnika, koncentrując się na częściach systemu, które są krytyczne lub podlegają częstym zmianom.

Początkowo sama wielkość bazy kodu będzie prawdopodobnie przytłaczająca w porównaniu z niewielką liczbą testów, ale nie ma podejścia do wielkiego wybuchu, a rozpoczęcie czegoś jest ważniejsze niż dręczenie się, jakie będzie najlepsze podejście.

Poleciłbym książkę Michaela Feathersa „ Skutecznie współpracując ze starszym kodem” , aby uzyskać kilka dobrych, szczegółowych porad.

Danny Woods
źródło
10
+1. Jeśli napiszesz testy na podstawie dokumentacji, odkryjesz wszelkie rozbieżności między działającym kodem a dokumentacją, co jest nieocenione.
Carl Manaster,
1
Kolejny powód dodawania testów: po znalezieniu błędu możesz łatwo dodać przypadek testowy do przyszłych testów regresji.
Jest to strategia opisana w (darmowym) kursie online edX CS169.2x w rozdziale o starszym kodzie. Jak mówią nauczyciele: „Ustalanie prawdy naziemnej za pomocą testów charakteryzacyjnych” w rozdziale 9 w książce: beta.saasbook.info/table-of-contents
FGM
21

Nie wszystkie testy jednostkowe przynoszą równe korzyści. Zaletą testu jednostkowego jest jego niepowodzenie. Im mniej prawdopodobne jest, że zawiedzie, tym mniej będzie to korzystne. Nowy lub ostatnio zmieniony kod jest bardziej podatny na błędy niż rzadko zmieniany kod, który jest dobrze przetestowany podczas produkcji. Dlatego testy jednostkowe nowego lub ostatnio zmienionego kodu są bardziej prawdopodobne.

Nie wszystkie testy jednostkowe mają taki sam koszt. O wiele łatwiej jest testować jednostkowo trywialny kod, który sam sobie zaprojektowałeś, niż złożony kod opracowany przez kogoś innego dawno temu. Ponadto testowanie podczas programowania zwykle oszczędza czas programowania. W przypadku kodu starszego typu oszczędności kosztów nie są już dostępne.

W idealnym świecie miałbyś cały czas na testowanie jednostkowego kodu starszego typu, ale w świecie rzeczywistym w pewnym momencie wydaje się, że koszty dodania testów jednostkowych do starszego kodu przeważą korzyści. Sztuką jest zidentyfikowanie tego punktu. Kontrola wersji może pomóc, wyświetlając ostatnio zmieniony i najczęściej zmieniany kod, i możesz zacząć od poddania ich testowi jednostkowemu. Ponadto, gdy wprowadzasz zmiany w przyszłości, poddaj te zmiany i ściśle powiązany kod testowi jednostkowemu.

Postępując zgodnie z tą metodą, w końcu będziesz mieć całkiem niezły zasięg w najbardziej korzystnych obszarach. Jeśli zamiast tego spędzasz miesiące na przeprowadzaniu testów jednostkowych przed wznowieniem działań generujących przychody, może to być pożądana decyzja dotycząca konserwacji oprogramowania, ale jest to kiepska decyzja biznesowa.

Karl Bielefeldt
źródło
3
Jeśli kod rzadko zawodzi, można poświęcić wiele czasu i wysiłku na znalezienie tajemniczych problemów, które nigdy nie mogłyby wystąpić w prawdziwym życiu. Z drugiej strony, jeśli kod zawiera błędy i jest podatny na błędy, prawdopodobnie możesz rozpocząć testowanie w dowolnym miejscu i natychmiast znaleźć problemy.
Robbie Dee,
8

Czy dodawanie testów to dobry pomysł?

Oczywiście, choć trochę trudno mi uwierzyć, że kod jest czysty i działa dobrze i używa nowoczesnych technik i po prostu nie ma testów jednostkowych. Czy na pewno nie siedzą w osobnym rozwiązaniu?

W każdym razie, jeśli zamierzasz rozszerzyć / utrzymać kod, prawdziwe testy jednostkowe są nieocenione w tym procesie.

Jeśli tak, to jak zacząć od czegoś takiego?

Krok po kroku. Jeśli nie znasz się na testowaniu jednostkowym, naucz się trochę. Po zapoznaniu się z koncepcjami wybierz jedną małą sekcję kodu i napisz dla niej testy. Potem następny i następny. Pokrycie kodu może pomóc Ci znaleźć miejsca, które przegapiłeś.

Najprawdopodobniej najlepiej jest wybrać niebezpieczne / ryzykowne / istotne rzeczy do przetestowania w pierwszej kolejności, ale możesz być bardziej skuteczny, testując coś od razu, aby wpaść najpierw w groove - szczególnie, jeśli ty / zespół nie jesteś przyzwyczajony do bazy kodu i / lub jednostki testowanie.

Telastyn
źródło
7
„Jesteś pewien, że nie siedzą w osobnym rozwiązaniu?” to świetne pytanie. Mam nadzieję, że OP tego nie przeoczy.
Dan Pichelman,
Niestety nie ma szans, aplikacja została uruchomiona wiele lat temu, gdy TDD zyskiwała przyczepność, więc w pewnym momencie zamierzano wykonać testy, ale z jakiegoś powodu po rozpoczęciu projektu nigdy tego nie dostali.
Paweł,
2
Prawdopodobnie nigdy tego nie dotarli, ponieważ zajęłoby im to dodatkowy czas, który nie był tego wart. Dobry programista pracujący sam może na pewno stworzyć czystą, przejrzystą, dobrze zorganizowaną i działającą aplikację o stosunkowo dużych rozmiarach bez żadnych zautomatyzowanych testów, i ogólnie może to zrobić szybciej i tak samo bezbłędnie, jak w przypadku testów. Ponieważ wszystko leży w ich głowie, prawdopodobieństwo błędów lub problemów organizacyjnych jest znacznie mniejsze niż w przypadku tworzenia go przez wielu programistów.
Ben Lee
3

Tak, posiadanie testów to dobry pomysł. Pomogą udokumentować istniejącą bazę kodu zgodnie z przeznaczeniem i wychwycą wszelkie nieoczekiwane zachowania. Nawet jeśli testy początkowo się nie powiodą, pozwól im, a następnie refaktoryzuj kod później, aby przejść i zachowywać się zgodnie z przeznaczeniem.

Rozpocznij pisanie testów dla mniejszych klas (tych, które nie mają zależności i są stosunkowo proste) i przejdź do większych klas (tych, które mają zależności i są bardziej złożone). Zajmie to dużo czasu, ale bądź cierpliwy i wytrwały, abyś w końcu mógł w jak największym stopniu objąć bazę kodów.

Bernard
źródło
Czy naprawdę dodałbyś testy zakończone niepowodzeniem do programu, który działa dobrze (mówi OP)?
MarkJ
Tak, ponieważ pokazuje, że coś nie działa zgodnie z przeznaczeniem i wymaga dalszej weryfikacji. To przyspieszy dyskusję i, mam nadzieję, poprawi wszelkie nieporozumienia lub wcześniej nieznane wady.
Bernard,
@Bernard - lub testy mogą ujawnić twoje niezrozumienie tego, co powinien zrobić kod. Testy napisane po fakcie grożą niepoprawnym zamknięciem pierwotnych zamiarów.
Dan Pichelman,
@DanPichelman: Zgadzam się, ale to nie powinno zniechęcać do pisania testów.
Bernard,
Jeśli nic więcej, oznaczałoby to, że kod nie został napisany obronnie.
Robbie Dee,
3

OK, wydam przeciwną opinię ....

Dodanie testów do istniejącego, działającego systemu spowoduje zmianę tego systemu, chyba że system jest napisany z myślą o kpinach od samego początku. Wątpię, choć jest całkiem możliwe, że ma dobre oddzielenie wszystkich komponentów z łatwo definiowalnymi granicami, w które można wsunąć swoje pozorne interfejsy. Ale jeśli tak nie jest, będziesz musiał dokonać dość znaczących (względnie mówiąc) zmian, które mogą zepsuć wszystko. W najlepszym przypadku poświęcisz mnóstwo czasu na napisanie tych testów, czas, który można lepiej napisać na napisanie szczegółowych dokumentów projektowych, dokumentów analizy wpływu lub dokumentów konfiguracji rozwiązania. W końcu to praca, którą szef chce zrobić więcej niż testy jednostkowe. Czyż nie

W każdym razie nie dodawałbym żadnych testów jednostkowych.

Skoncentrowałbym się na zewnętrznych, zautomatyzowanych narzędziach testowych, które zapewnią ci rozsądny zasięg bez zmiany. Potem, kiedy przychodzi się wprowadzać modyfikacje ... wtedy można rozpocząć dodawanie testów jednostkowych w bazie kodu.

gbjbaanb
źródło
2

Często spotkałem się z tą sytuacją, odziedziczyłem dużą bazę kodu bez odpowiedniego lub bez pokrycia testowego, a teraz jestem odpowiedzialny za dodawanie funkcjonalności, naprawianie błędów itp.

Radzę upewnić się i sprawdzić, co dodajesz, a jeśli naprawisz błędy lub zmienisz przypadki użycia w bieżącym kodzie, autor przeprowadzi testy. Jeśli musisz coś dotknąć, napisz testy w tym momencie.

Kiedy to się psuje, istniejący kod nie jest dobrze skonstruowany do testowania jednostkowego, więc spędzasz dużo czasu na refaktoryzacji, abyś mógł dodać testy dla drobnych zmian.

Casey
źródło