Czy testy jednostkowe nie powinny wykorzystywać własnych metod?

83

Dzisiaj oglądałem wideo „Podstawy JUnit ”, a autor powiedział, że podczas testowania danej metody w twoim programie nie powinieneś używać innych własnych metod.

Mówiąc ściślej, mówił o przetestowaniu jakiejś metody tworzenia rekordów, która wzięła nazwę i nazwisko dla argumentów i wykorzystała je do utworzenia rekordów w danej tabeli. Twierdził jednak, że w trakcie testowania tej metody nie powinien używać innych metod DAO do przeszukiwania bazy danych w celu sprawdzenia końcowego wyniku (aby sprawdzić, czy rekord rzeczywiście został utworzony z odpowiednimi danymi). Twierdził, że w tym celu powinien napisać dodatkowy kod JDBC w celu przeszukania bazy danych i sprawdzenia wyniku.

Myślę, że rozumiem ducha jego twierdzenia: nie chcesz, aby przypadek testowy jednej metody zależał od poprawności innych metod (w tym przypadku metody DAO), a osiąga się to poprzez napisanie (ponownie) własnej walidacji / kod pomocniczy (który powinien być bardziej szczegółowy i skoncentrowany, a zatem prostszy kod).

Niemniej jednak głosy w mojej głowie zaczęły protestować argumentami takimi jak powielanie kodu, niepotrzebne dodatkowe wysiłki itp. To znaczy, jeśli uruchomimy całą baterię testową i dokładnie przetestujemy wszystkie nasze metody publiczne (w tym w tym przypadku metodę DAO), nie powinny czy nie można po prostu użyć niektórych z tych metod podczas testowania innych? Jeśli jeden z nich nie robi tego, co powinien, wtedy jego własny test nie powiedzie się, a my możemy go naprawić i ponownie uruchomić baterię testową. Nie ma potrzeby kopiowania kodu (nawet jeśli zduplikowany kod jest nieco prostszy) ani zmarnowanych wysiłków.

Mam do tego silne przeczucie z powodu kilku ostatnio napisanych przeze mnie aplikacji Excel - VBA (poprawnie przetestowanych jednostkowo dzięki Rubberduck for VBA ), gdzie zastosowanie tego zalecenia oznaczałoby wiele dodatkowych prac, bez zauważalnej korzyści.

Czy możesz podzielić się swoimi spostrzeżeniami na ten temat?

carlossierra
źródło
79
Nieco dziwnie jest widzieć test jednostkowy, który w ogóle obejmuje bazę danych
Richard Tingle
4
IMO dobrze jest nazywać inne klasy IFF, które są wystarczająco szybkie. Wyśmiewać wszystko, co musi przejść na dysk lub przez sieć. Wyśmiewanie zwykłej IMO klasy nie ma sensu.
RubberDuck,
2
Masz link do tego filmu?
candied_orange
17
właściwie przetestowane jednostkowo dzięki Rubberduck dla VBA ” - proszę pana, właśnie zrobiłeś mi dzień. Naprawiłbym literówkę i edytowałem ją z „RubberDuck” na „Rubberduck”, ale czuję, że jakiś spamer robi to i dodaje link do Rubberduckvba.com (jestem właścicielem domeny i współwłaścicielem projektu z @RubberDuck) - więc skomentuję tutaj. W każdym razie to niesamowite, że ludzie faktycznie korzystają z narzędzia, które było odpowiedzialne za większość moich nieprzespanych nocy przez większą część ostatnich dwóch lat! =)
Mathieu Guindon
4
@ Mat'sMug and RubberDuck Uwielbiam to, co robisz z Rubberduck. Proszę, tak trzymaj. Z pewnością moje życie jest dzięki temu łatwiejsze (zdarza mi się robić wiele małych programów i prototypów w Excelu VBA). BTW, wzmianka o Rubberduck w moim poście była tylko tym, że starałem się być miły dla samego RubberDuck, co z kolei było dla mnie miłe tutaj w PE. :)
carlossierra

Odpowiedzi:

186

Duch jego roszczenia jest rzeczywiście poprawny. Celem testów jednostkowych jest izolacja kodu, przetestowanie go bez zależności, aby każde błędne zachowanie można było szybko rozpoznać w miejscu, w którym się dzieje.

Powiedziawszy to, testowanie jednostkowe jest narzędziem i ma służyć twoim celom, nie jest to ołtarz, o który należy się modlić. Czasami oznacza to pozostawienie zależności, ponieważ działają one wystarczająco niezawodnie, a Ty nie chcesz zawracać sobie głowy ich szydzeniem, czasem oznacza to, że niektóre z twoich testów jednostkowych są bardzo blisko, jeśli nie testy integracyjne.

Ostatecznie nie otrzymujesz oceny, najważniejszy jest produkt końcowy testowanego oprogramowania, ale musisz tylko pamiętać, kiedy naginasz zasady i decydujesz, kiedy kompromisy są tego warte.

Jaka jest nazwa?
źródło
117
Brawo dla pragmatyzmu.
Robert Harvey,
6
Dodam, że nie jest to pewna niezawodność, ale szybkość, która prowadzi do zlikwidowania zależności. Test w izolacji mantry jest tak przekonujący, że często nie można tego argumentu pominąć.
Michael Durrant,
4
„... testowanie jednostkowe jest narzędziem i ma służyć twoim celom, nie jest to ołtarz, o który trzeba się modlić.” - to!
Wayne Conrad,
15
„nie ołtarz do modlitwy” - nadchodzą wściekli trenerzy TDD!
Den
5
@ jpmc26: co gorsza, nie chcesz, aby wszystkie testy jednostkowe przeszły pomyślnie, ponieważ poprawnie obsługiwały one awarię usługi internetowej, co jest prawidłowym i poprawnym zachowaniem, ale nigdy tak naprawdę nie ćwiczy kodu, gdy jest włączony! Gdy go skubisz, możesz wybrać, czy ma być „w górę”, czy „w dół” i przetestować oba.
Steve Jessop,
36

Myślę, że sprowadza się to do terminologii. Dla wielu „test jednostkowy” jest bardzo specyficzną rzeczą i z definicji nie może mieć warunku zaliczenia / niepowodzenia, który zależy od dowolnego kodu poza testowaną jednostką (metoda, funkcja itp.). Obejmuje to interakcję z bazą danych.

Dla innych termin „test jednostkowy” jest znacznie luźniejszy i obejmuje wszelkiego rodzaju testy automatyczne, w tym kod testowy, który testuje zintegrowane części aplikacji.

Testowy purysta (jeśli mogę użyć tego terminu) może nazwać to testem integracyjnym , odróżniającym się od testu jednostkowego na tej podstawie, że test zależy od więcej niż jednej czystej jednostki kodu.

Podejrzewam, że używasz luźniejszej wersji terminu „test jednostkowy” i faktycznie odnosi się do „testu integracyjnego”.

Korzystając z tych definicji, nie powinieneś polegać na kodzie poza testowaną jednostką w „teście jednostkowym”. Jednak w „teście integracji” całkowicie uzasadnione jest napisanie testu, który wykonuje określony fragment kodu, a następnie sprawdza bazę danych pod kątem spełniania kryteriów.

To, czy powinieneś polegać na testach integracyjnych, testach jednostkowych, czy na obu, jest znacznie szerszym tematem dyskusji.

Eric King
źródło
Masz rację co do swoich podejrzeń. Nadużywam terminów i widzę, jak można lepiej obsługiwać każdy rodzaj testu. Dzięki!
carlossierra
11
„Dla innych termin„ test jednostkowy ”jest znacznie bardziej luźny” - poza czymkolwiek, tak zwane „ramy testowania jednostkowego” są naprawdę wygodnym sposobem organizowania i uruchamiania testów wyższego poziomu ;-)
Steve Jessop
1
Zamiast „czystego” vs „luźnego” rozróżnienia, które jest pełne konotacji, sugerowałbym, że rozróżnia się tych, którzy patrzą na „jednostki”, „zależności” itp. Z perspektywy domeny (np. Liczy się „uwierzytelnianie” jako jednostka „baza danych” liczyłaby się jako zależność itp.) w porównaniu z tymi, którzy patrzą na nią z perspektywy języka programowania (np. metody są jednostkami, klasy są zależnościami). Uważam, że określenie, co należy rozumieć przez wyrażenia takie jak „testowana jednostka”, może zamienić argumenty („Mylisz się!”) W dyskusje („Czy to jest odpowiedni poziom szczegółowości?”).
Warbo
1
@EricKing Oczywiście, dla bardzo dużej części osób z pierwszej kategorii sam pomysł włączenia bazy danych do testów jednostkowych jest przekleństwem, niezależnie od tego, czy korzystasz z DAO, czy bezpośrednio z bazy danych.
Periata Breatta
1
@AmaniKilumanga Pytanie było w zasadzie „ktoś mówi, że nie powinienem robić tego w testach jednostkowych, ale robię to w testach jednostkowych i myślę, że jest w porządku. Czy to w porządku?” A moja odpowiedź brzmiała: „Tak, ale większość osób nazwałaby to testem integracyjnym zamiast testem jednostkowym”. Jeśli chodzi o twoje pytanie, nie wiem, co rozumiesz przez „czy testy integracyjne mogą wykorzystywać metody kodu produkcyjnego?”.
Eric King,
7

Odpowiedź brzmi: tak i nie ...

Unikalne testy, które wykonują się w oderwaniu, są absolutną koniecznością, ponieważ pozwalają ustalić podstawy działania właściwego kodu niskiego poziomu. Ale przy kodowaniu większych bibliotek znajdziesz również obszary kodu, które będą wymagać testów krzyżujących jednostki.

Te testy między jednostkami są dobre pod kątem pokrycia kodu i podczas testowania kompleksowej funkcjonalności, ale mają kilka wad, o których powinieneś wiedzieć:

  • Bez pojedynczego testu potwierdzającego, co się psuje, nieudany test „cross-unit” będzie wymagał dodatkowego rozwiązywania problemów w celu ustalenia, co jest nie tak z twoim kodem
  • Zbyt duże poleganie na testach między jednostkami może sprawić, że przestaniesz myśleć o umowie, w której zawsze powinieneś pisać podczas pisania kodu SOLID Object-Oriented. Izolowane testy zwykle mają sens, ponieważ twoje jednostki powinny wykonywać tylko jedną podstawową akcję.
  • Kompleksowe testowanie jest pożądane, ale może być niebezpieczne, jeśli test wymaga zapisania do bazy danych lub wykonania czynności, których nie chciałbyś wykonywać w środowisku produkcyjnym. To jeden z wielu powodów, dla których frameworki takie jak Mockito są tak popularne, ponieważ pozwalają na sfałszowanie obiektu i naśladowanie testu od końca do końca bez zmiany czegoś, czego nie powinieneś.

Na koniec dnia chcesz mieć jedno i drugie ... dużo testów, które wychwytują funkcjonalność niskiego poziomu i kilka, które testują funkcjonalność od końca do końca. Skoncentruj się przede wszystkim na tym, dlaczego piszesz testy. Zapewniają one pewność, że kod działa zgodnie z oczekiwaniami. Wszystko, co musisz zrobić, aby tak się stało, jest w porządku.

Smutny, ale prawdziwy fakt jest taki, że jeśli w ogóle używasz automatycznych testów, to masz już dużą przewagę nad wieloma programistami. Dobre praktyki testowania są pierwszą rzeczą, która się zsuwa, gdy zespół programistów stoi przed trudnymi terminami. Tak długo, jak trzymasz się pistoletów i piszesz testy jednostkowe, które są znaczącym odzwierciedleniem tego, jak powinien działać twój kod, nie miałbym obsesji na punkcie tego, jak „czyste” są twoje testy.

Wilgotny
źródło
4

Jednym z problemów związanych z używaniem innych metod obiektowych do testowania warunku jest to, że przegapisz błędy, które się wzajemnie znoszą. Co ważniejsze, brakuje ci trudności z trudną implementacją testową, a ten ból uczy cię czegoś o twoim podstawowym kodzie. Bolesne testy oznaczają teraz bolesną konserwację później.

Zmniejsz swoje jednostki, podziel klasę, przebuduj i przeprojektuj, aż łatwiej będzie przetestować bez ponownej implementacji reszty obiektu. Uzyskaj pomoc, jeśli uważasz, że kodu lub testów nie można już uprościć. Spróbuj pomyśleć o koledze, który zdaje się zawsze mieć szczęście i otrzymywać zadania, które można łatwo i dokładnie przetestować. Poproś go o pomoc, ponieważ to nie jest szczęście.

Karl Bielefeldt
źródło
1
Klucz tutaj jest w jednostce. Jeśli potrzebujesz nawiązywać połączenia z innymi procedurami i procesami, nie byłoby to dla mnie jednostką. Jeśli wykonanych jest wiele wywołań i konieczne jest zatwierdzenie wielu kroków, to jeśli jest to duży fragment kodu niż jednostka, podziel go na mniejsze części obejmujące jeden element logiki. Normalnie wywołanie DB byłoby również kpione lub DB w pamięci byłby użyty w teście jednostkowym nad dostępem do rzeczywistej DB i potrzebą cofnięcia danych po teście.
dlb
1

Jeśli testowaną jednostką funkcjonalności jest „dane przechowywane trwale i możliwe do odzyskania”, to miałbym test jednostkowy, który - przechowuje w prawdziwej bazie danych, niszczy obiekty zawierające odniesienia do bazy danych, a następnie wywołuje kod, aby pobrać przedmioty z powrotem.

Testowanie, czy rekord jest tworzony w bazie danych, wydaje się dotyczyć szczegółów implementacji mechanizmu pamięci, a nie testowania jednostki funkcjonalności wystawionej na resztę systemu.

Możesz skrócić test, aby poprawić wydajność przy użyciu fałszywej bazy danych, ale miałem kontrahentów, którzy to dokładnie zrobili i zostawili na końcu umowy z systemem, który przeszedł takie testy, ale tak naprawdę nie przechowywał niczego w bazie danych między restartami systemu.

Można spierać się, czy „jednostka” w teście jednostkowym oznacza „jednostkę kodu” czy „jednostkę funkcjonalności”, funkcjonalność być może stworzoną przez wiele jednostek kodu wspólnie. Nie uważam tego za użyteczne rozróżnienie - pytania, o których pamiętam, brzmią: „czy test mówi ci coś o tym, czy system zapewnia wartość biznesową”, i „czy test jest kruchy, jeśli implementacja się zmieni?”. Testy takie jak opisywane są przydatne, gdy używasz TDD w systemie - nie napisałeś jeszcze „pobierz obiekt z rekordu bazy danych”, więc nie możesz przetestować pełnej jednostki funkcjonalności - ale jest kruchy wobec zmian implementacyjnych, więc chciałbym usuń je po przetestowaniu pełnej operacji.

Pete Kirkham
źródło
1

Duch ma rację.

Idealnie, w teście jednostkowym testujesz jednostkę (indywidualna metoda lub mała klasa).

Idealnie byłoby zniszczyć cały system bazy danych. To znaczy, uruchomiłbyś swoją metodę w fałszywym środowisku i po prostu upewnij się, że wywołuje ona poprawne API DB we właściwej kolejności. Wyraźnie, pozytywnie nie chcesz testować bazy danych podczas testowania jednej z własnych metod.

Korzyści jest wiele. Przede wszystkim testy szybko się oślepiają, ponieważ nie musisz zawracać sobie głowy konfigurowaniem poprawnego środowiska DB i późniejszym jego wycofywaniem.

Oczywiście jest to wzniosły cel w każdym projekcie oprogramowania, który nie robi tego od samego początku. Ale jest to duch testów jednostkowych .

Należy pamiętać, że istnieją inne testy, takie jak testy funkcji, testy zachowania itp., Które różnią się od tego podejścia - nie należy mylić „testowania” z „testowaniem jednostkowym”.

AnoE
źródło
„po prostu upewnij się, że wywołuje prawidłowe interfejsy API DB we właściwej kolejności” - tak, jasne. Dopóki nie okaże się, że źle odczytałeś dokumentację, a rzeczywista poprawna kolejność, z której powinieneś skorzystać, jest zupełnie inna. Nie kpij z systemów poza twoją kontrolą.
Joker_vD,
4
@Joker_vD Do tego służą testy integracyjne. W testach jednostkowych absolutnie należy wyśmiewać systemy zewnętrzne.
Ben Aaronson,
1

Istnieje co najmniej jeden bardzo ważny powód, aby nie używać bezpośredniego dostępu do bazy danych w testach jednostkowych dla kodu biznesowego współdziałającego z bazą danych: jeśli zmienisz implementację bazy danych, będziesz musiał przepisać wszystkie te testy jednostkowe. Zmień nazwę kolumny, w kodzie wystarczy zmienić jeden wiersz w definicji mapera danych. Jeśli jednak nie użyjesz mapera danych podczas testowania, przekonasz się, że musisz także zmienić każdy test jednostkowy, który odwołuje się do tej kolumny . Może to przerodzić się w nadzwyczajną ilość pracy, szczególnie w przypadku bardziej złożonych zmian, które są mniej podatne na wyszukiwanie i zastępowanie.

Ponadto użycie mapera danych jako abstrakcyjnego mechanizmu zamiast bezpośredniego komunikowania się z bazą danych ułatwia całkowite usunięcie zależności od bazy danych , co może nie być teraz istotne, ale gdy wykonasz tysiące testów jednostkowych i wszystkich z nich uderzają w bazę danych, będziesz wdzięczny, że możesz z łatwością refaktoryzować je, aby usunąć tę zależność, ponieważ Twój zestaw testowy spadnie z minut na uruchomienie do sekund, co może mieć ogromną korzyść dla wydajności.

Twoje pytanie wskazuje, że myślisz właściwie. Jak podejrzewasz, testy jednostkowe są również kodem. Równie ważne jest, aby były łatwe w utrzymaniu i łatwe do dostosowania do przyszłych zmian, tak jak w pozostałej części kodu. Staraj się, aby były one czytelne i eliminować powielanie, a będziesz mieć znacznie lepszy zestaw testów. Dobry zestaw testowy to taki, który się przyzwyczaja. A zestaw testów, który się przyzwyczaja, pomaga znaleźć błędy, a taki, który się nie wykorzystuje, jest po prostu bezwartościowy.

Periata Breatta
źródło
1

Najlepszą lekcją, jakiej nauczyłem się podczas testowania testów jednostkowych i testów integracyjnych, jest nie testowanie metod, ale zachowanie testowe. Innymi słowy, co robi ten obiekt?

Kiedy patrzę na to w ten sposób, metoda, która utrwala dane, i inna metoda, która odczytuje je z powrotem, zaczyna być testowalna. Jeśli testujesz metody konkretnie, oczywiście kończy się testem dla każdej metody oddzielnie, takim jak:

@Test
public void canSaveData() {
    writeDataToDatabase();
    // what can you assert - the only expectation you can have here is that an exception was not thrown.
}

@Test
public void canReadData() {
    // how do I even get data in there to read if I cannot call the method which writes it?
}

Ten problem występuje z powodu perspektywy metod testowania. Nie testuj metod. Testuj zachowania. Jakie jest zachowanie klasy Widgetexe? Utrzymuje się widżety. Ok, w jaki sposób sprawdzisz, czy utrzymuje się widżety? Jaka jest definicja trwałości? Oznacza to, że kiedy go napiszesz, możesz przeczytać go ponownie. Więc czytaj + pisz razem stają się testem i, moim zdaniem, bardziej znaczącym testem.

@Test
public void widgetsCanBeStored() {
    Widget widget = new Widget();
    widget.setXXX.....
    // blah

    widgetDao.storeWidget(widget);
    Widget stored = widgetDao.getWidget(widget.getWidgetId());
    assertEquals(widget, stored);
}

To logiczny, spójny, niezawodny i moim zdaniem znaczący test.

Inne odpowiedzi koncentrują się na tym, jak ważna jest izolacja, debata pragmatyczna kontra realistyczna oraz czy test jednostkowy może zapytać do bazy danych. Jednak tak naprawdę nie odpowiadają na pytanie.

Aby sprawdzić, czy coś można zapisać, musisz je zapisać, a następnie ponownie przeczytać. Nie możesz sprawdzić, czy coś jest przechowywane, jeśli nie możesz go przeczytać. Nie testuj przechowywania danych oddzielnie od ich odzyskiwania. Skończysz z testami, które nic ci nie mówią.

Brandon
źródło
Bardzo wnikliwe podejście i, jak mówisz, myślę, że naprawdę trafiłeś w jedną z głównych wątpliwości, które miałem. Dzięki!
carlossierra
0

Jednym z przykładów niepowodzenia, który chciałbyś przechwycić, jest to, że testowany obiekt wykorzystuje warstwę buforującą, ale nie zachowuje danych zgodnie z wymaganiami. Następnie, jeśli zapytasz obiekt, powie: „tak, mam nową nazwę i adres”, ale chcesz, aby test się nie powiódł, ponieważ tak naprawdę nie zrobił tego, co powinien.

Alternatywnie (i pomijając naruszenie zasady pojedynczej odpowiedzialności) załóżmy, że wymagane jest zachowanie wersji łańcucha zakodowanej w UTF-8 w polu zorientowanym na bajty, ale tak naprawdę trwa Shift JIS. Niektóre inne komponenty będą czytać bazę danych i spodziewa się zobaczyć UTF-8, stąd wymóg. Następnie podróż w obie strony przez ten obiekt zgłosi poprawną nazwę i adres, ponieważ przekonwertuje go z powrotem z Shift JIS, ale błąd nie zostanie wykryty przez twój test. Miejmy nadzieję, że zostanie wykryty w późniejszym teście integracji, ale głównym celem testów jednostkowych jest wczesne wychwycenie problemów i dokładna znajomość odpowiedzialnego komponentu.

Jeśli jeden z nich nie robi tego, co powinien, wtedy jego własny test nie powiedzie się, a my możemy go naprawić i ponownie uruchomić baterię testową.

Nie możesz tego założyć, ponieważ jeśli nie będziesz ostrożny, napiszesz zestaw testów zależnych od siebie. „Czy to oszczędza?” test wywołuje testowaną metodę zapisu, a następnie metodę ładowania, aby potwierdzić zapisanie. „Czy ładuje się?” test wywołuje metodę save, aby skonfigurować urządzenie testowe, a następnie testuje metodę ładowania, aby sprawdzić wynik. Oba testy opierają się na poprawności metody, której nie testują, co oznacza, że ​​żaden z nich nie sprawdza poprawności metody, którą testuje.

Problem polega na tym, że dwa testy, które rzekomo testują różne jednostki, faktycznie robią to samo . Oboje wywołują settera, a następnie gettera, a następnie sprawdzają, czy wynikiem jest oryginalna wartość. Ale chciałeś sprawdzić, czy ustawiający utrzymuje dane, a nie, czy para ustawiający / pobierający działa razem. Więc wiesz, że coś jest nie tak, musisz tylko dowiedzieć się, co i naprawić testy.

Jeśli Twój kod jest dobrze zaprojektowany do testowania jednostkowego, istnieją co najmniej dwa sposoby sprawdzenia, czy dane rzeczywiście zostały poprawnie utrwalone przez testowaną metodę:

  • wyśmiewaj interfejs bazy danych i pozwól, aby twój próbny zapis, że wywołano w nim odpowiednie funkcje, z oczekiwanymi wartościami. Ta metoda sprawdza, czy metoda wykonuje to, co powinna, i jest klasycznym testem jednostkowym.

  • przekazać rzeczywistą bazę danych z dokładnie tym samym zamiarem , aby zarejestrować, czy dane zostały poprawnie utrwalone. Ale zamiast mieć wyśmiewaną funkcję, która mówi tylko „tak, mam właściwe dane”, twój test odczytuje bezpośrednio z bazy danych i potwierdza, że ​​jest poprawny. To może nie być najczystszy możliwy test, ponieważ cały silnik bazy danych jest dość dużą rzeczą do napisania uwielbianej makiety, z większą szansą, że przeoczę jakąś subtelność, która sprawia, że ​​test przechodzi pomyślnie, chociaż coś jest nie tak (na przykład ja nie powinienem używać tego samego połączenia z bazą danych do odczytu, co zostało użyte do zapisu, ponieważ mogę zobaczyć niezatwierdzoną transakcję). Ale testuje właściwą rzecz, a przynajmniej wiesz, że to dokładnie implementuje cały interfejs bazy danych bez konieczności pisania fałszywego kodu!

Jest to więc tylko szczegół implementacji testowej, niezależnie od tego, czy czytam dane z testowej bazy danych przez JDBC, czy też kpię z bazy danych. Tak czy inaczej chodzi o to, że mogę lepiej przetestować jednostkę, izolując ją, niż mogę, jeśli pozwolę, by spiskowała z innymi niepoprawnymi metodami w tej samej klasie, aby wyglądała dobrze, nawet jeśli coś jest nie tak. Dlatego chcę użyć wszelkich dogodnych środków, aby sprawdzić, czy poprawne dane zostały utrwalone, innych niż zaufanie komponentowi, którego metodę testuję.

Jeśli Twój kod nie jest dobrze zaprojektowany do testowania jednostkowego, możesz nie mieć wyboru, ponieważ obiekt, którego metodę chcesz przetestować, może nie zaakceptować bazy danych jako wstrzykniętej zależności. W takim przypadku dyskusja na temat najlepszego sposobu odizolowania testowanej jednostki zmienia się w dyskusję na temat tego, jak blisko jest możliwe izolowanie testowanej jednostki. Wniosek jest taki sam. Jeśli możesz uniknąć spisku między wadliwymi jednostkami, robisz to, z zastrzeżeniem dostępnego czasu i wszystkiego, co według ciebie byłoby bardziej skuteczne w wykrywaniu błędów w kodzie.

Steve Jessop
źródło
0

Tak lubię to robić. To tylko osobisty wybór, ponieważ nie wpłynie to na wynik produktu, a jedynie sposób na jego wytworzenie.

Zależy to od możliwości dodawania fałszywych wartości do bazy danych bez testowanej aplikacji.

Załóżmy, że masz dwa testy: A - odczyt bazy danych B - wstaw do bazy danych (zależy od A)

Jeśli A pass, to masz pewność, że wynik B będzie zależał od części testowanej w B, a nie od zależności. Jeśli A zawiedzie, możesz mieć fałszywie ujemny wynik dla B. Błąd może być w B lub A. Nie możesz być pewien, dopóki A nie zwróci sukcesu.

Nie próbowałbym pisać zapytań podczas testów jednostkowych, ponieważ nie powinieneś być w stanie poznać struktury DB stojącej za aplikacją (lub może się zmienić). Oznacza to, że przypadek testowy może się nie powieść z powodu samego kodu. Chyba że napiszesz test do testu, ale kto przetestuje test do testu ...

AxelH
źródło
-1

Ostatecznie wszystko sprowadza się do tego, co chcesz przetestować.

Czasami wystarczy przetestować dostarczony interfejs klasy (np. Rekord jest tworzony i zapisywany i można go odzyskać później, być może po „ponownym uruchomieniu”). W takim przypadku dobrze jest (i zwykle dobrym pomysłem) użyć dostarczonych metod do testowania. Umożliwia to ponowne użycie testów, na przykład jeśli chcesz zmienić mechanizm przechowywania (inna baza danych, baza danych w pamięci do testowania, ...).

Zauważ, że oznacza to, że naprawdę zależy ci tylko na tym, że klasa przestrzega swojej umowy (w tym przypadku tworzy, przechowuje i pobiera rekordy), a nie jak to osiąga. Jeśli inteligentnie utworzysz testy jednostkowe (bezpośrednio na interfejsie, a nie na klasie), możesz przetestować różne implementacje, ponieważ muszą one również przestrzegać umowy interfejsu.

Z drugiej strony, w niektórych przypadkach musisz faktycznie sprawdzić, w jaki sposób rzeczy się utrwalają (może jakiś inny proces polega na tym, że dane mają odpowiedni format w tej bazie danych?). Teraz nie możesz polegać tylko na odzyskaniu właściwego rekordu z klasy, zamiast tego musisz sprawdzić, czy jest on poprawnie zapisany w bazie danych. A najbardziej bezpośrednim sposobem na to jest zapytanie samej bazy danych.

Teraz dbasz nie tylko o klasę przestrzegającą jej umowy (tworzenie, przechowywanie i pobieranie), ale także o to, jak to osiąga (ponieważ utrwalone dane muszą przylegać do innego interfejsu). Jednak teraz testy zależą zarówno od interfejsu klasy, jak i formatu pamięci, więc trudno będzie je w pełni wykorzystać. (Niektórzy mogą nazwać tego rodzaju testy „testami integracji”).

hoffmale
źródło
@downvoters: czy mógłbyś podać mi swoje powody, abym mógł poprawić aspekty, które według ciebie są pozbawione mojej odpowiedzi?
hoffmale