Więc dzisiaj rozmawiałem z kolegą z zespołu na temat testów jednostkowych. Wszystko zaczęło się, gdy zapytał mnie „hej, gdzie są testy dla tej klasy, widzę tylko jeden?”. Cała klasa była menedżerem (lub usługą, jeśli wolisz tak ją nazywać) i prawie wszystkie metody po prostu delegowały rzeczy do DAO, więc było to podobne do:
SomeClass getSomething(parameters) {
return myDao.findSomethingBySomething(parameters);
}
Rodzaj płyty kotłowej bez logiki (lub przynajmniej nie uważam takiej prostej delegacji za logikę), ale przydatny płytkę kotłową w większości przypadków (separacja warstw itp.). I mieliśmy dość długą dyskusję, czy powinienem to przetestować jednostkowo (myślę, że warto wspomnieć, że w pełni przetestowałem DAO). Jego głównymi argumentami jest to, że nie był to TDD (oczywiście) i że ktoś może chcieć zobaczyć test, aby sprawdzić, co robi ta metoda (nie wiem, jak to może być bardziej oczywiste) lub że w przyszłości ktoś może chcieć zmienić implementacja i dodaj do niej nową (lub bardziej jak „dowolną”) logikę (w takim przypadku chyba ktoś powinien po prostu przetestować tę logikę ).
To jednak zmusiło mnie do myślenia. Czy powinniśmy dążyć do jak najwyższego odsetka pokrycia testowego? A może jest to po prostu sztuka dla samej sztuki? Po prostu nie widzę żadnego powodu do testowania takich rzeczy jak:
- pobierające i ustawiające (chyba że mają w sobie logikę)
- kod „płyty kotłowej”
Oczywiście test na taką metodę (z próbami) zająłby mi mniej niż minutę, ale wydaje mi się, że to wciąż zmarnowany czas i milisekunda dłużej dla każdego CI.
Czy istnieją jakieś racjonalne / nie „łatwopalne” powody, dla których należy testować każdą (lub jak najwięcej) linię kodu?
źródło
Odpowiedzi:
Kieruję się zasadą Kent Kent Beck:
Przetestuj wszystko, co może się zepsuć.
Oczywiście jest to do pewnego stopnia subiektywne. Dla mnie trywialne gettery / settery i one-linery takie jak twoje powyżej zwykle nie są tego warte. Ale z drugiej strony spędzam większość czasu na pisaniu testów jednostkowych starszego kodu, tylko marzę o fajnym projekcie TDD od podstaw ... W takich projektach reguły są inne. W przypadku starszego kodu głównym celem jest objęcie jak największej liczby obszarów przy jak najmniejszym wysiłku, więc testy jednostkowe są zwykle na wyższym poziomie i są bardziej złożone, bardziej jak testy integracyjne, jeśli ktoś jest pedantyczny w zakresie terminologii. A kiedy walczysz o zwiększenie zasięgu kodu z 0% lub po prostu udało Ci się go podbić o ponad 25%, moduły pobierające i ustawiające testy jednostkowe są najmniejszymi zmartwieniami.
OTOH w projekcie TDD typu greenfield, może być bardziej rzeczowym pisanie testów nawet dla takich metod. Zwłaszcza, że napisałeś już test, zanim zaczniesz zastanawiać się „czy ta jedna linia jest warta dedykowanego testu?”. Przynajmniej te testy są trywialne do napisania i szybkie w uruchomieniu, więc i tak nie jest to wielka sprawa.
źródło
Istnieje kilka rodzajów testów jednostkowych:
Gdyby najpierw napisać test, miałoby to większy sens - tak jak można by się spodziewać nazywania warstwy dostępu do danych. Test początkowo się nie powiedzie. Następnie napiszesz kod produkcyjny, aby test przeszedł pomyślnie.
Idealnie powinieneś testować kod logiczny, ale interakcje (obiekty wywołujące inne obiekty) są równie ważne. W twoim przypadku zrobiłbym to
Obecnie nie ma tam logiki, ale nie zawsze tak będzie.
Jeśli jednak masz pewność, że w tej metodzie nie będzie logiki i prawdopodobnie pozostanie taka sama, rozważę wywołanie warstwy dostępu do danych bezpośrednio od konsumenta. Zrobiłbym to tylko, jeśli reszta zespołu jest na tej samej stronie. Nie chcesz wysyłać niewłaściwej wiadomości do zespołu, mówiąc: „Cześć, można zignorować warstwę domeny, wystarczy bezpośrednio wywołać warstwę dostępu do danych”.
Skoncentrowałbym się również na testowaniu innych komponentów, gdyby istniał test integracji dla tej metody. Ale jeszcze nie widziałem firmy z solidnymi testami integracji.
Powiedziawszy to wszystko - nie będę ślepo testować wszystkiego. Ustanowiłbym gorące punkty (komponenty o dużej złożoności i wysokim ryzyku pęknięcia). Następnie skoncentrowałbym się na tych elementach. Nie ma sensu posiadanie podstawy kodu, w której 90% podstawy kodu jest dość proste i jest objęte testami jednostkowymi, podczas gdy pozostałe 10% reprezentuje podstawową logikę systemu i nie są objęte testami jednostkowymi ze względu na ich złożoność.
Wreszcie, jaka jest korzyść z przetestowania tej metody? Jakie są konsekwencje, jeśli to nie zadziała? Czy są katastroficzne? Nie staraj się uzyskać wysokiego zasięgu kodu. Pokrycie kodu powinno być wynikiem dobrego zestawu testów jednostkowych. Na przykład możesz napisać jeden test, który przejdzie drzewo i da ci 100% pokrycia tą metodą, lub możesz napisać trzy testy jednostkowe, które dadzą ci również 100% pokrycia. Różnica polega na tym, że pisząc trzy testy testujesz przypadki skrajne, a nie tylko chodzisz po drzewie.
źródło
Oto dobry sposób, aby pomyśleć o jakości oprogramowania:
W przypadku funkcji typu bojler i trywialnych zadań możesz polegać na sprawdzaniu typu, wykonując swoje zadanie, a dla reszty potrzebujesz przypadków testowych.
źródło
Moim zdaniem złożoność cykliczna jest parametrem. Jeśli metoda nie jest wystarczająco złożona (np. Pobierające i ustawiające). Testy jednostkowe nie są potrzebne. Poziom złożoności cyklicznej McCabe'a powinien być większy niż 1. Innym słowem powinno być minimum 1 instrukcja blokowa.
źródło
Rozległy TAK z TDD (i kilkoma wyjątkami)
Kontrowersyjne w porządku, ale twierdzę, że każdemu, kto odpowie „nie” na to pytanie, brakuje podstawowej koncepcji TDD.
Dla mnie odpowiedź brzmi tak, jeśli postępujesz zgodnie z TDD. Jeśli nie, to nie jest wiarygodną odpowiedzią.
DDD w TDD
TDD jest często cytowany jako mający główne zalety.
Oddziel odpowiedzialność od wdrożenia
Jako programiści strasznie kusi myślenie o atrybutach jako o czymś istotnym, a getters i seter o czymś w rodzaju kosztów ogólnych.
Ale atrybuty są szczegółami implementacji, podczas gdy setery i gettery są umownym interfejsem, który faktycznie powoduje, że programy działają.
O wiele ważniejsze jest przeliterowanie, że obiekt powinien:
i
następnie w jaki sposób ten stan jest faktycznie przechowywany (dla którego atrybut jest najczęstszy, ale nie jedyny sposób).
Test taki jak
jest ważny dla części dokumentacji TDD.
Pisząc test, fakt, że ostateczna implementacja jest trywialna (atrybut) i nie przynosi żadnej korzyści obronnej, nie powinien być dla ciebie nieznany.
Brak inżynierii w obie strony ...
Jednym z kluczowych problemów w świecie rozwoju systemu jest brak inżynierii w obie strony 1 - proces rozwoju systemu jest podzielony na niepowiązane podprocesy, których artefakty (dokumentacja, kod) są często niespójne.
1 Brodie, Michael L. „John Mylopoulos: szycie nasion modelowania koncepcyjnego”. Modelowanie koncepcyjne: podstawy i zastosowania. Springer Berlin Heidelberg, 2009. 1-9.
... i jak TDD to rozwiązuje
Jest to część dokumentacji TDD, która zapewnia, że specyfikacje systemu i jego kodu są zawsze spójne.
Najpierw zaprojektuj, a później zaimplementuj
W TDD najpierw piszemy test akceptacji zakończony niepowodzeniem, a dopiero potem kod, który pozwala im przejść.
W BDD wyższego poziomu najpierw piszemy scenariusze, a następnie je przekazujemy.
Dlaczego warto wykluczyć seterów i getterów?
Teoretycznie jedna osoba może napisać test w TDD, a druga wdrożyć kod, który go zdaje.
Więc zadaj sobie pytanie:
Ponieważ metody pobierające i ustawiające są publicznym interfejsem dla klasy, odpowiedź brzmi oczywiście tak , lub nie będzie możliwości ustawienia ani zapytania o stan obiektu.
Oczywiście, jeśli najpierw napiszesz kod, odpowiedź może nie być tak jednoznaczna.
Wyjątki
Istnieją pewne oczywiste wyjątki od tej reguły - funkcje, które są wyraźnymi szczegółami implementacyjnymi i wyraźnie nie są częścią projektu systemu.
Na przykład metoda lokalna „B ()”:
Lub funkcja prywatna
square()
tutaj:Lub dowolna inna funkcja, która nie jest częścią
public
interfejsu wymagającego pisowni w projekcie komponentu systemu.źródło
W obliczu filozoficznego pytania powróć do wymagań dotyczących jazdy.
Czy Twoim celem jest wytwarzanie oprogramowania w miarę niezawodnego po konkurencyjnych kosztach?
A może ma na celu wytwarzanie oprogramowania o najwyższej możliwej niezawodności prawie niezależnie od kosztów?
Do pewnego stopnia dwa cele: jakość i szybkość rozwoju / koszty są wyrównane: spędzasz mniej czasu na pisaniu testów niż na naprawianiu wad.
Ale poza tym nie robią tego. Nie jest tak trudno dostać, powiedzmy, jeden zgłoszony błąd na programistę miesięcznie. Zmniejszenie tego o połowę do jednego na dwa miesiące zwalnia budżet na dzień lub dwa, a dodatkowe testy prawdopodobnie nie zmniejszą o połowę wskaźnika defektów. Nie jest to więc zwykła wygrana / wygrana; musisz to uzasadnić na podstawie kosztów wady dla klienta.
Koszt ten będzie się różnił (a jeśli chcesz być zły, ich zdolność do zwrócenia Ci tych kosztów, czy to przez rynek, czy przez pozew). Nie chcesz być zły, więc odliczasz te koszty w całości; czasami niektóre testy wciąż globalnie pogarszają świat z powodu ich istnienia.
Krótko mówiąc, jeśli spróbujesz ślepo zastosować te same standardy do wewnętrznej witryny internetowej, co oprogramowanie do lotów samolotów pasażerskich, skończysz albo z działalnością, albo w więzieniu.
źródło
Twoja odpowiedź na to zależy od twojej filozofii (wierzysz, że to Chicago vs Londyn? Jestem pewien, że ktoś to sprawdzi). Jury jest nadal przy tym w kwestii najbardziej efektywnego czasowo podejścia (ponieważ w końcu jest to największy czynnik, który poświęca mniej czasu na poprawki).
Niektóre podejścia mówią, że testuj tylko interfejs publiczny, inne - testuj kolejność każdego wywołania funkcji w każdej funkcji. Stoczono wiele świętych wojen. Radzę wypróbować oba podejścia. Wybierz jednostkę kodu i zrób to jak X, a drugą jak Y. Po kilku miesiącach testów i integracji wróć i sprawdź, który z nich lepiej pasuje do twoich potrzeb.
źródło
To trudne pytanie.
Ściśle mówiąc, powiedziałbym, że nie jest to konieczne. Lepiej jest pisać testy w stylu BDD i testy poziomu systemu, które zapewniają, że wymagania biznesowe funkcjonują zgodnie z założeniami w scenariuszach pozytywnych i negatywnych.
To powiedziawszy, jeśli twoja metoda nie jest objęta tymi przypadkami testowymi, musisz najpierw zapytać, dlaczego ona istnieje i czy jest potrzebna, lub czy w kodzie są ukryte wymagania, które nie są odzwierciedlone w twojej dokumentacji lub historiach użytkowników, które powinien być zakodowany w walizce testowej typu BDD.
Osobiście lubię utrzymywać zasięg według linii na poziomie około 85-95% i odprawy bramkowe do linii głównej, aby zapewnić, że istniejące pokrycie testem jednostkowym na linię osiągnie ten poziom dla wszystkich plików kodu i że nie zostaną odkryte żadne pliki.
Zakładając, że przestrzegane są najlepsze praktyki testowania, daje to duży zasięg, nie zmuszając deweloperów do marnowania czasu na próby uzyskania dodatkowego pokrycia trudnego do wykonania kodu lub trywialnego kodu tylko ze względu na zasięg.
źródło
Problemem jest samo pytanie: nie musisz testować wszystkich „metod” ani wszystkich „klas”, których potrzebujesz, aby przetestować wszystkie funkcje swoich systemów.
Kluczowe myślenie w kategoriach cech / zachowań zamiast myślenia w kategoriach metod i klas. Oczywiście jest tutaj metoda zapewniająca obsługę jednej lub więcej funkcji, na końcu cały kod jest testowany, a przynajmniej cały kod ma znaczenie w bazie kodu.
W twoim scenariuszu prawdopodobnie ta klasa „menedżera” jest zbędna lub niepotrzebna (jak wszystkie klasy o nazwie zawierającej słowo „menedżer”), a może nie, ale wydaje się, że jest to szczegół implementacyjny, prawdopodobnie ta klasa nie zasługuje na jednostkę test, ponieważ ta klasa nie ma żadnej logiki biznesowej. Prawdopodobnie potrzebujesz tej klasy, aby jakaś funkcja działała, test tej funkcji obejmuje tę klasę, w ten sposób możesz refaktoryzować tę klasę i mieć test sprawdzający, czy rzecz, która ma znaczenie, twoje cechy, nadal działa po refaktorze.
Pomyśl o cechach / zachowaniach, a nie o klasach metod, nie mogę powtórzyć tego wystarczająco dużo razy.
źródło
Tak, najlepiej 100%, ale niektórych rzeczy nie da się przetestować jednostkowo.
Getters / Setters są głupi - po prostu ich nie używaj. Zamiast tego umieść zmienną członka w sekcji publicznej.
Pobierz wspólny kod i przetestuj go. To powinno być tak proste.
Nie robiąc tego, możesz przegapić kilka bardzo oczywistych błędów. Testy jednostkowe są jak bezpieczna sieć do przechwytywania określonych rodzajów błędów i należy z niej korzystać tak często, jak to możliwe.
I ostatnia rzecz: jestem przy projekcie, w którym ludzie nie chcieli tracić czasu na pisanie testów jednostkowych dla jakiegoś „prostego kodu”, ale później postanowili w ogóle nie pisać. Na końcu fragmenty kodu zamieniły się w wielką kulę błota .
źródło