Czy dodanie testów jednostkowych ma sens w przypadku dobrze znanego starszego kodu?

21
  • Mówię o testach jednostkowych w sensie TDD. (Nie zautomatyzowana „integracja” lub jak to nazywasz testami).
  • Stary kod jak w: (C ++) bez testów. (patrz: Efektywna współpraca Michaela Feathersa ze starszym kodem )
  • Ale także starszy kod jak w: Kod, nad którym nasz zespół pracuje od 10-5 lat, więc bardzo często mamy całkiem dobre pojęcie, gdzie należy coś zmienić.
  • Prowadzimy testy jednostkowe (za pośrednictwem Boost.Test) dla niektórych modułów, które pojawiły się później lub były „naturalnym” dopasowaniem do testów jednostkowych (typowe pojemniki specyficzne dla aplikacji, łańcuchy, pomoce sieciowe itp.)
  • Nie mamy jeszcze odpowiednich automatycznych testów akceptacyjnych.

Teraz ostatnio miałem „przyjemność” zaimplementować 3 nowe funkcje zorientowane na użytkownika.

Każda z nich zajęła mi około 1-2 godzin przygotowania się do pracy z częściami kodu, które musiałem zmienić, 1-2 godzinami na wdrożenie (małego) kodu, który musiałem zmienić, i kolejne 1-2 godziny, aby upewnić się, że aplikacja działał poprawnie później i zrobił to, co miał zrobić.

Teraz naprawdę dodałem trochę kodu. (Myślę, że dla każdej funkcji jest jedna metoda i kilka linii wywoławczych).

Wyodrębnienie tego kodu (dowolną z metod sugerowanych w WEwLC ), tak aby test jednostkowy miał sens (a nie była kompletną tautologią), zajęłoby z łatwością kolejne 2-4 godziny, jeśli nie więcej. Wydłużyłoby to 50–100% czasu do każdej funkcji, bez natychmiastowych korzyści, jak

  1. Nie potrzebowałem testu jednostkowego, aby zrozumieć cokolwiek na temat kodu
  2. Ręczne testowanie to ta sama ilość pracy, ponieważ wciąż muszę sprawdzić, czy kod jest poprawnie zintegrowany z resztą aplikacji.

Oczywiście, jeśli później ktoś „przyjdzie” i dotknie tego kodu, teoretycznie mógłby skorzystać z tego testu jednostkowego. (Tylko teoretycznie, ponieważ ta przetestowana wyspa kodu żyłaby w oceanie nieprzetestowanego kodu).

Tak więc „tym razem” nie zdecydowałem się na dodanie ciężkiej pracy polegającej na dodaniu testu jednostkowego: zmiany w kodzie w celu przetestowania tych rzeczy byłyby znacznie bardziej skomplikowane niż zmiany w kodzie w celu prawidłowego (i czystego) wdrożenia funkcji.

Czy jest to coś typowego dla mocno sprzężonego starszego kodu? Czy jestem leniwy / czy jako zespół ustalamy niewłaściwe priorytety? Czy też jestem ostrożny, testując tylko te rzeczy, w których koszty ogólne nie są zbyt wysokie?

Martin Ba
źródło
co jeśli inny dział przejmie „dobrze znany starszy kod”? Co tam zrobią faceci?
Alex Theodoridis

Odpowiedzi:

18

Musisz być pragmatyczny w tych sytuacjach. Wszystko musi mieć wartość biznesową, ale firma musi ci zaufać, aby ocenić, jaka jest wartość pracy technicznej. Tak, zawsze jest korzyść z przeprowadzania testów jednostkowych, ale czy ta korzyść jest wystarczająco duża, aby uzasadnić spędzony czas?

Zawsze kłóciłbym się o nowy kod, ale w przypadku kodu starszego należy wykonać orzeczenie.

Czy często przebywasz w tym obszarze kodu? Potem jest potrzeba ciągłego doskonalenia. Czy dokonujesz znaczącej zmiany? Potem jest przypadek, że jest to już nowy kod. Ale jeśli tworzysz jednowierszowy kod w złożonym obszarze, który prawdopodobnie nie zostanie ponownie dotknięty przez rok, oczywiście koszt (nie wspominając o ryzyku) ponownej inżynierii jest zbyt duży. Wystarczy umieścić tam jeden wiersz kodu i szybko wziąć prysznic.

Zasada: zawsze myślę sobie: „ Czy uważam, że firma czerpie więcej korzyści z tej pracy technicznej, którą, jak sądzę, powinnam wykonać, niż praca, o którą poprosili, która w rezultacie może zostać opóźniona?

pdr
źródło
Co to jest dobry kawałek kodu? Taki, który jest dobrze zaprojektowany, przetestowany, udokumentowany i przyszłościowy, czy taki, za który otrzymujesz zapłatę? Jak zawsze: możesz mieć to dobrze ; możesz mieć to tanie ; możesz mieć to szybko . Wybierz dowolne dwa, trzeci to twój koszt.
Sardathrion - Przywróć Monikę
2
„Zawsze kłóciłbym się o nowy kod” ... „nowy kod” w bazie kodu starszego typu lub „nowy nowy kod”? :-)
Martin Ba
pdr ma właściwy pomysł. To decyzja inżynierska (z kompromisami). Ogólnie, jeśli masz duży sprawdzony kod, dobrym pomysłem jest pozostawienie go w spokoju. Widziałem wysiłki TDD w zakresie starszego kodu (tylko rok, ale to wystarczy) ostatecznie kosztują miesiące i skończyło się na tym, że wydawałem pieniądze i łamałem starszą aplikację przez wiele miesięcy, dopóki błędy wprowadzone przez refaktor TDD nie zostały naprawione. Jeśli kod / funkcja jest wykonana i przetestowana, nie psuj jej, aby dodać oprzyrządowanie (TDD), aby powiedzieć ci coś, co i tak już wiedziałeś (że działa).
anon
10

Zdobycie testów jednostkowych w sensie TDD polega na uzyskaniu czegoś, co będzie samo w sobie, bez potrzeby ustnej tradycji budowanej przez lata przez stabilny zespół.

To wielkie szczęście, że ten sam zespół pracował od wielu lat nad tym samym projektem. Ale to się ostatecznie zmieni. Ludzie chorują, nudzą się lub awansują. Poruszają się, przechodzą na emeryturę lub umierają. Nowe przychodzą z nowymi pomysłami i chęcią refaktoryzacji wszystkiego, czego się nauczyli w szkole. Firmy są sprzedawane i kupowane. Zmieniają się zasady zarządzania.

Jeśli chcesz, aby Twój projekt przetrwał, musisz go przygotować na zmiany.

mouviciel
źródło
3

Pisanie testów jednostkowych jest przyszłym zabezpieczeniem kodu. Jeśli baza kodu nie zawiera testów, należy dodać nowe testy dla wszystkich nowych funkcji, ponieważ dzięki temu ten fragment kodu jest trochę bardziej niezawodny.

Uważam, że dodawanie testów, nawet w starszym kodzie, jest korzystne w perspektywie średnio- i długoterminowej. Jeśli Twoja firma nie dba o średnio- i długoterminową przyszłość, szukałbym innej firmy. Ponadto nie sądzę, aby testowanie ręczne trwało dłużej niż pisanie zestawu testów jednostkowych. Jeśli tak, musisz poćwiczyć kata kodu skoncentrowane na pisaniu testów.

Sardathrion - Przywróć Monikę
źródło
1
Ale, ale, ale! :-) To niewiele powinno przełożyć się na 100% wzrost czasu potrzebnego na wdrożenie nowej funkcji. To nie nie zmniejszyć czas potrzebny na wdrożenie kolejnych funkcji. Może to skrócić czas potrzebny na zmianę rzeczy.
Martin Ba
3
Dbasz o to, aby zmiany w tym obszarze kodu nie wprowadzały nowych błędów, aby nowi (mniej doświadczeni użytkownicy) mogli dobrze zrozumieć kod, a działania niepożądane wpływające na ten obszar kaszlały w czasie testowania zamiast w momencie wydania czas. Testowanie pomaga zweryfikować, czy kod robi to, co powinien, nie ułatwia późniejszego dodawania funkcji. Uważam, że dodawanie testów, nawet w starszym kodzie, jest korzystne w perspektywie średnio- i długoterminowej . Jeśli Twoja firma nie dba o średnio- i długoterminową przyszłość, szukałbym innej firmy.
Sardathrion - Przywróć Monikę
1
@Martin, ponieważ wyraźnie to, co robisz, to kodowanie według tego, co powinna być, a następnie wysyłanie. Na żadnym etapie nie przeprowadza się żadnych testów ... nie, czekaj. Jako programiści wszyscy testujemy nasz kod (przynajmniej ręcznie), zanim stwierdzimy, że jest gotowy. Zastąpienie „ręcznie” słowem „przez napisanie automatycznego testu” nie oznacza 100% wzrostu czasu. Często uważam, że to mniej więcej tyle samo czasu.
Kaz Dragon
1
@Kaz - „Ręczne zastąpienie” słowem „przez napisanie automatycznego testu” nie oznacza 100-procentowego wydłużenia czasu. Często stwierdzam, że zajmuje to tyle samo czasu ”. - YMMV, ale dla mojej bazy kodowej jest to oczywiście błędne (jak określono w pytaniu).
Martin Ba
3
@Kaz: Co miałem na myśli :-): Test jednostkowy testuje mój kod w izolacji. Jeśli nie piszę testu jednostkowego, nie testuję kodu osobno, ale tylko wtedy, gdy kod jest wywoływany poprawnie i daje pożądany wynik w aplikacji. Te dwa punkty (zwane poprawnie i aplikacja robi to, co powinno) muszę mimo to przetestować (ręcznie) i nie są objęte testem jednostkowym. Test jednostkowy nie obejmowałby tych dwóch punktów, ale „po prostu” testowałby moją funkcję w izolacji. (A te testy jednostkowe byłyby dodatkowe i nie byłyby niczym równoważone przez wszystko, co widzę.)
Martin Ba
2

Z pewnością najlepsze praktyki nakazują testowanie jednostkowe każdego zmienianego kodu. Kodowanie w świecie rzeczywistym wymaga jednak wielu kompromisów, czas i materiały są ograniczone.

To, co musisz zrobić, to oszacować koszt w kategoriach naprawiania błędów i wprowadzania modyfikacji bez testów jednostkowych. Następnie możesz ocenić inwestycję w tworzenie tych testów. Testy jednostkowe znacznie zmniejszają koszty przyszłej pracy, ale teraz mają swoją cenę.

Podsumowując, jeśli twój system jest rzadko zmieniany, może nie warto pisać testów jednostkowych, ale jeśli podlega regularnym zmianom, warto.

Tom Squires
źródło
1

Wszystkie te małe racjonalne decyzje, aby nie tworzyć testów, budują paraliżujący dług techniczny, który powróci, by cię prześladować, gdy ktoś odejdzie, a nowy wynajmujący musi przyjść i przyspieszyć.

Tak, napisanie testów jednostkowych może kosztować kolejne 2-3 godziny, ale jeśli kod zostanie odesłany z testu, będziesz musiał ponownie ręcznie przetestować wszystko i udokumentować testowanie - czyż nie? W przeciwnym razie skąd ktoś wie, co przetestowałeś i jakich wartości użyłeś?

Testy jednostkowe dokumentują oczekiwane zachowanie kodu danych testowych użytych do sprawdzenia funkcjonalności i dają nowym programistom wystarczającą pewność, że niczego nie zepsuły podczas wprowadzania zmian.

Kosztuje dziś kilka godzin więcej niż testowanie ręczne, ale następnie testujesz za każdym razem, gdy wprowadzasz jakiekolwiek zmiany, oszczędzając godziny w trakcie życia kodu.

Jeśli jednak wiesz, że kod zostanie wycofany za kilka lat, wszystko, co powiedziałem, zmieniło się, ponieważ nie dokończysz testowania kodu i nigdy się nie zwróci.

Mcottle
źródło
„i jeszcze raz udokumentuj swoje testy - prawda?” - {zakładając cyniczny kapelusz:} Oczywiście, że nie. Nie jesteśmy w krainie deweloperów. Rzeczy są wdrażane, testowane i wysyłane. Później zostaje zepsuty, naprawiony, przetestowany i wysłany.
Martin Ba
Mam nadzieję, że wszystko zostanie przetestowane ręcznie, niezależnie od liczby testów jednostkowych. Zautomatyzowane testy akceptacyjne to zupełnie inna sprawa. Jednak testy akceptacyjne nie są trudniejsze, ponieważ podstawa kodu jest starsza, więc nie wchodzą w zakres.
pdr
2
mcottle - Brakuje istotnego punktu. Testowanie jednostkowe nie skróci czasu wykonywania „ręcznego testowania funkcjonalności” gotowej funkcji. I jeszcze trzeba klikalności wszystkich okien dialogowych związanych z ostrożnej podzbiór możliwych wejść do upewnij się, że rzeczywisty gotową aplikację jako zbudowany robi co to robić. (Przyznaję, że teoretycznie testowanie jednostkowe może pomóc ograniczyć ten ręczny cykl testowy do tylko jednej pętli, jeśli testy jednostkowe upewnią się, że nie znajdę żadnego oczywistego problemu tylko podczas testowania ręcznego.)
Martin Ba
0

Wartość testów jednostkowych polega nie tylko na testowaniu nowej funkcji podczas jej opracowywania. Korzyści z testów jednostkowych uzyskuje się głównie w przyszłości.

Tak, być może trzeba będzie wydać co wydaje się jak dużej ilości czasu, aby twój kod Legacy sprawdzalne.

Ale jeśli tego nie zrobisz , poświęcisz , a na pewno powinieneś , poświęcić wiele, wiele, wiele więcej godzin na testowanie tej funkcji. Nie tylko w początkowej fazie rozwoju, ale wraz z każdą nową wersją oprogramowania. Bez testów jednostkowych i innych form testów automatycznych nie masz innej opcji, niż spędzić wiele godzin na testach ręcznych, aby upewnić się, że twoja funkcja nie została przypadkowo uszkodzona, nawet jeśli sama ta funkcja nie została zmieniona.

Marjan Venema
źródło
1
„Bez testów jednostkowych nie masz żadnych innych środków, aby upewnić się, że twoja funkcja nie została przypadkowo uszkodzona, nawet jeśli sama ta funkcja nie została zmieniona.” - Testy jednostkowe nie pomogą. Aby się upewnić , musiałbym mieć zautomatyzowane testy integracji / akceptacji.
Martin Ba
Tak, testy jednostkowe to tylko pierwszy krok. Zredagowano odpowiedź w celu wyjaśnienia
Marjan Venema