Co rozumie się pod „jednostką” w testach jednostkowych

9

Jak rozumiem teoretycznie pod „jednostką” ludzie oznaczają metodę (w OOP). Ale w praktyce testy, które weryfikują jakąś metodę w oderwaniu, są bardzo delikatnymi testami zachowania (weryfikacja nie wyniku, ale fakt, że wywołano jakąś metodę zależności). Widzę więc wielu ludzi, którzy przez jednostkę rozumieją mały zestaw ściśle powiązanych klas. W tym przypadku wyśmiewane / usuwane są tylko zewnętrzne zależności, a dla zależności, które są wewnątrz jednostki, stosuje się rzeczywiste implementacje. W tym przypadku jest więcej testów stanu, znaczących (zgodnie ze specyfikacją) i nie tak delikatnych. Pytanie brzmi: co sądzisz o tych podejściach i czy warto nazywać testowanie jednostkowe drugiego podejścia, czy może jest to jakiś test integracji niskiego poziomu?

Jeśli zobaczysz jakieś szczególne uwagi na temat zastosowania TDD w jednym z tych sposobów testowania, byłbym wdzięczny za twoje przemyślenia.

SiberianGuy
źródło

Odpowiedzi:

11

Pierwotnie TDD wywodził się ze zwinnego ruchu, w którym testowanie zostało napisane z góry jako sposób na upewnienie się, że kodowanie pozostało poprawne, biorąc pod uwagę specyfikację, która została teraz dobrze zdefiniowana w kodzie testowym. Okazało się to również bardzo ważnym aspektem refaktoryzacji, ponieważ kiedy modyfikowałeś swój kod, możesz polegać na testach, aby udowodnić, że nie zmieniłeś zachowania kodu.

Potem pojawiły się narzędzia, które ludzie myśleli, że znają informacje o twoim kodzie, a następnie mogli wygenerować kody testowe, aby pomóc ci w pisaniu testów jednostkowych, i myślę, że tutaj wszystko poszło nie tak.

Kody testowe są generowane przez komputer, który nie ma pojęcia, co robisz, po prostu bezmyślnie tworzy kod pośredni dla każdej metody, ponieważ tak właśnie powinno być. Oznacza to, że masz przypadek testowy dla każdej metody, niezależnie od złożoności tej metody lub tego, czy nadaje się ona do testowania w izolacji.

Wynika to z testowania z niewłaściwego końca metodologii TDD. W TDD powinieneś dowiedzieć się, co ma zrobić kod, a następnie stworzyć kod, który to osiągnie. Jest to samospełniające się, ponieważ kończy się pisaniem testów, które dowodzą, że kod robi to, co robi, a nie to, co powinien. W połączeniu z automatycznym generowaniem kodów testowych opartych na metodach, marnujesz czas na udowadnianie każdego drobnego aspektu kodu, który może tak łatwo okazać się błędny, gdy wszystkie małe elementy zostaną złożone razem.

Kiedy Fowler opisał testowanie w swojej książce, odniósł się do testowania każdej klasy za pomocą własnej głównej metody. Udoskonalił to, ale koncepcja jest nadal taka sama - testujesz całą klasę, więc działa ona jako całość, wszystkie twoje testy są powiązane, aby udowodnić interakcję wszystkich tych metod, aby klasa mogła być ponownie użyta z określonymi oczekiwaniami.

Myślę, że zestawy narzędzi testowych zrobiły nam krzywdę, poprowadziły nas na ścieżkę myślenia, że ​​zestaw narzędzi jest jedynym sposobem na robienie rzeczy, kiedy naprawdę musisz przemyśleć więcej, aby uzyskać najlepszy wynik z kodu. Ślepe umieszczanie kodu testowego w skrótach testowych dla drobnych elementów oznacza po prostu, że musisz powtórzyć swoją pracę w teście integracyjnym (a jeśli zamierzasz to zrobić, dlaczego nie pominąć całkowicie teraz nadmiarowego etapu testowania jednostek). Oznacza to również, że ludzie tracą dużo czasu na uzyskanie 100% pokrycia testowego, a także dużo czasu na tworzenie dużej ilości szydzącego kodu i danych, które lepiej byłoby spędzić, ułatwiając test integracji kodu (tj. Jeśli masz tak dużo zależności danych, test jednostkowy może nie być najlepszą opcją)

Wreszcie, kruchość testów jednostkowych opartych na metodzie po prostu pokazuje problem. Refaktoryzacja jest przeznaczona do użycia z testami jednostkowymi, jeśli twoje testy ciągle się psują, ponieważ refaktoryzujesz, to coś poszło poważnie nie tak z całym podejściem. Refaktoryzacja lubi tworzyć i usuwać metody, więc oczywiście podejście oparte na ślepej próbie na metodę nie jest pierwotnie zamierzone.

Nie mam wątpliwości, że wiele metod otrzyma dla nich testy, wszystkie publiczne metody klasy powinny zostać przetestowane, ale nie można oderwać się od koncepcji testowania ich razem w ramach jednego przypadku testowego. Na przykład, jeśli mam metodę set i get, mogę pisać testy, które wprowadzają dane i sprawdzają, czy elementy wewnętrzne są ustawione poprawnie, lub mogę użyć każdego z nich, aby wstawić jakieś dane, a następnie pobrać je ponownie, aby sprawdzić, czy jest to wciąż to samo i nie zniekształcone. To testuje klasę, a nie każdą metodę oddzielnie. Jeśli seter polega na prywatnej metodzie pomocnika, to jest w porządku - nie musisz kpić z prywatnej metody, aby upewnić się, że seter działa, nie jeśli przetestujesz całą klasę.

Wydaje mi się, że religia zajmuje się tym tematem, dlatego widzisz schizmę w tak zwanym rozwoju opartym na zachowaniu i testowaniu - pierwotna koncepcja testowania jednostkowego dotyczyła rozwoju opartego na zachowaniu.

gbjbaanb
źródło
10

Jednostka jest najczęściej definiowana jako „ najmniejsza testowalna część aplikacji ”. Najczęściej oznacza to metodę. I tak, oznacza to, że nie powinieneś testować wyniku metody zależnej, ale po prostu, że metoda jest wywoływana (a następnie tylko raz, jeśli to możliwe, nie w każdym teście dla tej metody).

Nazywasz to kruchym. Myślę, że to nieprawda. Kruche testy to te, które psują się przy najmniejszej zmianie niepowiązanego kodu. Oznacza to, że opierają się na kodzie, który nie ma znaczenia dla testowanej funkcjonalności.

Myślę jednak, że tak naprawdę chcesz powiedzieć, że testowanie metody bez żadnych zależności nie jest dokładne. W tej kwestii zgodziłbym się. Potrzebne są również testy integracyjne, aby upewnić się, że jednostki kodu są poprawnie połączone ze sobą w celu utworzenia aplikacji.

Jest to dokładnie problem, który planuje rozwiązać rozwój oparty na zachowaniu , a zwłaszcza rozwój oparty na testach akceptacyjnych . Ale to nie eliminuje potrzeby testów jednostkowych / rozwoju opartego na testach; po prostu to uzupełnia.

pdr
źródło
Naprawdę chciałem powiedzieć, że testy zachowania są kruche. Często stają się fałszywie ujemne podczas zmiany bazy kodu. Z testami stanowymi dzieje się mniej (ale testy stanowe są bardzo rzadkie w przypadku testów jednostkowych)
SiberianGuy
@Isa: Jestem trochę zagubiony w twoich definicjach. Testy behawioralne to testy integracyjne, testujące określone zachowanie. Czytając pierwotne pytanie, wydaje się, że mówiąc o testach państwowych masz na myśli to samo.
pdr
przez stan rozumiem test, który weryfikuje stan, wynik jakiejś funkcji; przez zachowanie rozumiem test, który nie weryfikuje wyniku, ale fakt, że została wywołana jakaś funkcja
SiberianGuy
@Isa: W takim razie całkowicie się nie zgadzam. To, co nazywacie testem stanu, nazywam integracją. To, co nazywacie zachowaniem, nazywam jednostką. Testy integracyjne z samej swojej definicji są bardziej kruche. Google „jednostka testowa integracji kruchy”, a zobaczysz, że nie jestem sam.
pdr
istnieje dziennik artykułów na temat testowania, ale które z nich podzielają Twoją opinię?
SiberianGuy
2

Jak sama nazwa wskazuje, testujesz obiekt atomowy w każdym teście. Taki przedmiot jest zwykle pojedynczą metodą. Wiele testów może przetestować tę samą metodę, aby objąć szczęśliwą ścieżkę, możliwe błędy itp. Testujesz zachowanie, a nie mechanikę wewnętrzną. Testowanie jednostkowe polega zatem na testowaniu publicznego interfejsu klasy, czyli konkretnej metody.

W testach jednostkowych metodę należy przetestować w oderwaniu, tzn. Poprzez zubożenie / wyszydzenie / sfałszowanie dowolnych zależności. W przeciwnym razie testowanie jednostki z „rzeczywistymi” zależnościami czyni ją testem integracji. Jest czas i miejsce na oba typy testów. Testy jednostkowe zapewniają, że pojedynczy przedmiot działa zgodnie z oczekiwaniami, niezależnie. Testy integracyjne zapewniają, że „prawdziwe” podmioty działają razem poprawnie.

Grant Palin
źródło
1
nie całkiem, jednostka jest jakimkolwiek odizolowanym podmiotem, tylko dlatego, że zautomatyzowane oprzyrządowanie woli traktować metodę, ponieważ to nie czyni jej tak, ani nie czyni z niej najlepszej. Kluczem jest tutaj „izolowany”. Nawet jeśli testujesz metody, powinieneś również przetestować te prywatne.
gbjbaanb,
1

Moja ogólna zasada: najmniejsza jednostka kodu, która wciąż jest wystarczająco złożona, aby zawierać błędy.

Niezależnie od tego, czy jest to metoda, klasa czy podsystem, zależy od konkretnego kodu, nie można podać żadnej ogólnej reguły.

Na przykład nie zapewnia żadnej wartości do testowania prostych metod getter / setter w izolacji ani metod otoki, które wywołują tylko inną metodę. Nawet cała klasa może być zbyt prosta do przetestowania, jeśli klasa jest tylko cienkim opakowaniem lub adapterem. Jeśli jedyną rzeczą do przetestowania jest wywołanie metody na próbce, testowany kod jest cienki.

W innych przypadkach jedna metoda może wykonać skomplikowane obliczenia, które są cenne do przetestowania w izolacji.

W wielu przypadkach złożonymi częściami nie są poszczególne klasy, ale integracja między klasami. Więc testujesz dwie lub więcej klas jednocześnie. Niektórzy powiedzą, że nie są to testy jednostkowe, ale testy integracyjne, ale nie mówiąc o terminologii: powinieneś przetestować, gdzie jest złożoność, a testy te powinny być częścią zestawu testów.

JacquesB
źródło