Istnieją odpowiedzi na pytanie, w jaki sposób klasy testowe łączą się z bazą danych, np. „Powinny połączyć klasy testowe usługi ...” i „Testowanie jednostkowe - aplikacja połączona z bazą danych” .
Krótko mówiąc, załóżmy, że masz klasę A, która musi połączyć się z bazą danych. Zamiast pozwolić A faktycznie się połączyć, udostępniasz interfejsowi A, którego A może użyć do połączenia. Do testowania implementujesz ten interfejs z pewnymi rzeczami - oczywiście bez podłączania. Jeśli klasa B tworzy instancję A, musi przekazać „rzeczywiste” połączenie z bazą danych do A. Ale to oznacza, że B otwiera połączenie z bazą danych. Oznacza to, że do testu B wstrzykujesz połączenie do B. Ale B jest tworzone w klasie C i tak dalej.
Więc w którym momencie muszę powiedzieć „tutaj pobieram dane z bazy danych i nie będę pisać testu jednostkowego dla tego fragmentu kodu”?
Innymi słowy: Gdzieś w kodzie w jakiejś klasy I muszą zadzwonić sqlDB.connect()
lub coś podobnego. Jak przetestować tę klasę?
Czy to samo dotyczy kodu, który musi obsługiwać GUI lub system plików?
Chcę zrobić test jednostkowy. Jakikolwiek inny test nie ma związku z moim pytaniem. Wiem, że przetestuję tylko jedną klasę (zgadzam się z tobą Kilian). Teraz niektóre klasy muszą połączyć się z bazą danych. Jeśli chcę przetestować tę klasę i zapytać „Jak to zrobić”, wielu mówi: „Użyj wstrzykiwania zależności!” Ale to tylko przenosi problem na inną klasę, prawda? Pytam więc, jak mam przetestować klasę, która naprawdę ustanawia połączenie?
Dodatkowe pytanie: niektóre odpowiedzi tutaj sprowadzają się do „Użyj fałszywych obiektów!” Co to znaczy? Kpię z klas, od których zależy testowana klasa. Czy mam teraz wyśmiewać testowaną klasę i faktycznie testować próbę (która jest bliska idei użycia metod szablonów, patrz poniżej)?
źródło
Odpowiedzi:
Celem testu jednostkowego jest przetestowanie jednej klasy (w rzeczywistości powinno to zwykle przetestować jedną metodę ).
Oznacza to, że kiedy testujesz klasę
A
, wstrzykujesz do niej testową bazę danych - coś samodzielnie napisanego lub błyskawiczną bazę danych w pamięci, niezależnie od tego, co zrobi zadanie.Jeśli jednak przetestujesz klasę
B
, która jest klientemA
, zwykle kpisz z całegoA
obiektu czymś innym, prawdopodobnie czymś, co wykonuje swoją pracę w prymitywny, wstępnie zaprogramowany sposób - bez użycia rzeczywistegoA
obiektu i na pewno bez użycia danych base (chyba żeA
przekazuje całe połączenie bazy danych z powrotem do swojego rozmówcy - ale to tak okropne, że nie chcę o tym myśleć). Podobnie, pisząc test jednostkowy dla klasyC
, która jest klientemB
, wyśmiewałbyś coś, co pełni rolęB
, iA
całkowicie o nim zapominasz .Jeśli tego nie zrobisz, nie będzie to już test jednostkowy, ale test systemowy lub test integracji. Te są również bardzo ważne, ale zupełnie inny czajnik ryb. Po pierwsze, zwykle wymagają większego wysiłku, aby skonfigurować i uruchomić, nie jest praktyczne wymaganie przekazania ich jako warunku wstępnego zameldowania itp.
źródło
Wykonywanie testów jednostkowych dla połączenia z bazą danych jest całkowicie normalne i jest powszechną praktyką. Po prostu nie można stworzyć
purist
podejścia, w którym wszystko w twoim systemie można wstrzykiwać w zależności.Kluczem tutaj jest przetestowanie tymczasowej lub testowej bazy danych i jak najlżejszy proces uruchamiania w celu zbudowania testowej bazy danych.
Do testowania jednostkowego w CakePHP są tak zwane
fixtures
. Urządzenia to tymczasowe tabele bazy danych tworzone w locie na potrzeby testu jednostkowego. Urządzenie ma dogodne metody ich tworzenia. Mogą odtworzyć schemat z produkcyjnej bazy danych w testowej bazie danych lub zdefiniować schemat za pomocą prostej notacji.Kluczem do sukcesu jest nie wdrożenie biznesowej bazy danych, ale skupienie się tylko na aspekcie testowanego kodu. Jeśli masz test jednostkowy, który weryfikuje, czy model danych odczytuje tylko opublikowane dokumenty, wówczas schemat tabeli dla tego testu powinien zawierać tylko pola wymagane przez ten kod. Nie musisz ponownie wdrażać całej bazy danych zarządzania treścią, aby przetestować ten kod.
Kilka dodatkowych odniesień.
http://en.wikipedia.org/wiki/Test_fixture
http://phpunit.de/manual/3.7/en/database.html
http://book.cakephp.org/2.0/en/development/testing.html#fixtures
źródło
Gdzieś w twojej bazie kodu jest wiersz kodu, który wykonuje rzeczywistą akcję połączenia ze zdalną bazą danych. Ten wiersz kodu jest 9 razy na 10 wywołaniem „wbudowanej” metody dostarczanej przez biblioteki środowiska wykonawczego specyficzne dla Twojego języka i środowiska. Jako taki nie jest to „twój” kod, więc nie musisz go testować; na potrzeby testu jednostkowego możesz mieć pewność, że to wywołanie metody będzie działać poprawnie. To, co możesz i powinieneś nadal testować w swoim pakiecie testów jednostkowych, to takie, jak upewnienie się, że parametry, które będą używane dla tego wywołania, są zgodne z oczekiwaniami, takie jak upewnienie się, że parametry połączenia są poprawne, instrukcja SQL lub nazwa procedury składowanej.
Jest to jeden z celów stojących za ograniczeniem, że testy jednostkowe nie powinny pozostawiać ich środowiska uruchomieniowego „piaskownicy” i być zależne od stanu zewnętrznego. To właściwie całkiem praktyczne; celem testu jednostkowego jest sprawdzenie, czy kod, który napisałeś (lub masz zamiar napisać, w TDD) zachowuje się tak, jak się spodziewałeś. Kod, którego nie napisałeś, taki jak biblioteka, której używasz do wykonywania operacji na bazie danych, nie powinien wchodzić w zakres żadnego testu jednostkowego, z bardzo prostego powodu, że go nie napisałeś.
W pakiecie testów integracji ograniczenia te są złagodzone. Teraz już mogęprojektuj testy, które dotykają bazy danych, aby upewnić się, że napisany kod działa dobrze z kodem, którego nie zrobiłeś. Te dwa pakiety testowe powinny jednak pozostać posegregowane, ponieważ pakiet testów jednostkowych jest bardziej skuteczny, im szybciej działa (dzięki czemu można szybko sprawdzić, czy wszystkie twierdzenia deweloperów dotyczące ich kodu są nadal aktualne), a prawie z definicji test integracji jest wolniejszy o rząd wielkości ze względu na dodatkowe zależności od zasobów zewnętrznych. Pozwól build-botowi obsługiwać uruchamianie pełnego pakietu integracyjnego co kilka godzin, wykonując testy blokujące zasoby zewnętrzne, aby programiści nie stawali sobie na nogi, uruchamiając te same testy lokalnie. A jeśli kompilacja się zepsuje, co z tego? Dużo większą wagę przywiązuje się do zapewnienia, że build-bot nigdy nie zawiedzie kompilacji, niż prawdopodobnie powinno być.
To, jak ściśle możesz się do tego zastosować, zależy od dokładnej strategii łączenia się z bazą danych i wysyłania do niej zapytań. W wielu przypadkach, w których musisz używać szkieletowej struktury dostępu do danych, na przykład obiektów SqlConnection i SqlStatement ADO.NET, cała opracowana przez Ciebie metoda może składać się z wbudowanych wywołań metod i innego kodu zależnego od posiadania połączenie z bazą danych, dlatego najlepszym rozwiązaniem w tej sytuacji jest wyśmiewanie się z całej funkcji i zaufanie do pakietów testowych integracji. Zależy to również od tego, jak chętnie zaprojektujesz swoje klasy, aby umożliwić zastąpienie określonych wierszy kodu do celów testowych (takich jak sugestia Tobiego dotycząca wzorca metody szablonów, co jest dobre, ponieważ pozwala na „częściowe kpiny”
Jeśli model trwałości danych opiera się na kodzie w warstwie danych (takim jak wyzwalacze, przechowywane procy itp.), To po prostu nie ma innego sposobu na wykonanie kodu, który sam piszesz, niż opracowanie testów, które albo żyją wewnątrz warstwy danych, albo przekraczają granica między środowiskiem uruchomieniowym aplikacji a systemem DBMS. Z tego powodu purysta powiedziałby, że tego wzoru należy unikać na rzecz czegoś w rodzaju ORM. Nie sądzę, żebym posunął się tak daleko; nawet w dobie zapytań zintegrowanych z językiem i innych sprawdzanych przez kompilator operacji trwałości zależnych od domeny widzę wartość w blokowaniu bazy danych tylko do operacji ujawnionych za pomocą procedury składowanej, i oczywiście takie procedury przechowywane muszą być zweryfikowane za pomocą zautomatyzowanego testy. Ale takie testy nie są testami jednostkowymi . Są integracją testy.
Jeśli masz problem z tym rozróżnieniem, zwykle opiera się ono na dużym znaczeniu przywiązywanym do pełnego „pokrycia kodu”, czyli „pokrycia testu jednostkowego”. Chcesz się upewnić, że każdy wiersz kodu jest objęty testem jednostkowym. Szlachetny cel na pierwszy rzut oka, ale mówię, że to miazga; ta mentalność nadaje się do anty-wzorów wykraczających daleko poza ten konkretny przypadek, takich jak pisanie testów bez asercji, które wykonują, ale nie ćwicząTwój kod. Tego rodzaju przebiegi końcowe wyłącznie ze względu na numery ubezpieczenia są bardziej szkodliwe niż złagodzenie minimalnego zakresu ubezpieczenia. Jeśli chcesz mieć pewność, że każda linia bazy kodu jest wykonywana przez jakiś automatyczny test, to jest łatwe; podczas obliczania wskaźników zasięgu kodu należy uwzględnić testy integracyjne. Możesz nawet pójść o krok dalej i odizolować te sporne testy „Itino” („Integracja tylko w nazwie”), a między pakietem testów jednostkowych a tą podkategorią testów integracyjnych (które powinny nadal działać dość szybko) powinieneś dostać cholerę prawie blisko pełnego zasięgu.
źródło
Testy jednostkowe nigdy nie powinny łączyć się z bazą danych. Z definicji powinni testować każdą jednostkę kodu (metodę) w całkowitej izolacji od reszty systemu. Jeśli nie, to nie są testem jednostkowym.
Oprócz semantyki istnieje wiele powodów, dla których jest to korzystne:
Testy jednostkowe są sposobem na sprawdzenie pracy. Powinny one nakreślić wszystkie scenariusze dla danej metody, co zazwyczaj oznacza wszystkie różne ścieżki danej metody. To twoja specyfikacja, którą budujesz, podobna do księgowości z podwójnym wpisem.
Opisujesz inny typ testu automatycznego: test integracji. Chociaż są one również bardzo ważne, idealnie będzie ich o wiele mniej. Powinny sprawdzić, czy grupa jednostek prawidłowo się ze sobą integruje.
Jak więc testować rzeczy z dostępem do bazy danych? Cały kod dostępu do danych powinien znajdować się w określonej warstwie, aby kod aplikacji mógł wchodzić w interakcję z usługami możliwymi do naśladowania zamiast z rzeczywistą bazą danych. Nie powinno obchodzić się, czy te usługi są wspierane przez dowolny typ bazy danych SQL, dane testowe w pamięci, a nawet dane zdalnej usługi internetowej. To nie ich troska.
Idealnie (i to jest bardzo subiektywne), chcesz, aby większość kodu była objęta testami jednostkowymi. Daje to pewność, że każdy element działa niezależnie. Po zbudowaniu elementów musisz je połączyć. Przykład - kiedy hashuję hasło użytkownika, powinienem uzyskać dokładnie ten wynik.
Powiedzmy, że każdy element składa się z około 5 klas - chciałbyś przetestować wszystkie punkty awarii w nich zawarte. Oznacza to znacznie mniej testów, aby upewnić się, że wszystko jest prawidłowo okablowane. Przykład - test możesz znaleźć użytkownika z bazy danych, podając nazwę użytkownika / hasło.
Wreszcie, chcesz, aby niektóre testy akceptacyjne rzeczywiście zapewniły osiągnięcie celów biznesowych. Jest ich jeszcze mniej; mogą upewnić się, że aplikacja działa i robi to, co została stworzona. Przykład - biorąc pod uwagę te dane testowe, powinienem móc się zalogować.
Pomyśl o tych trzech rodzajach testów jak o piramidzie. Potrzebujesz wielu testów jednostkowych, aby wesprzeć wszystko, a następnie stamtąd wspinasz się.
źródło
Metoda szablonowa może pomóc.
Zawijasz wywołania do bazy danych
protected
metodami. Aby przetestować tę klasę, faktycznie testujesz fałszywy obiekt, który dziedziczy po prawdziwej klasie połączenia z bazą danych i zastępuje chronione metody.W ten sposób rzeczywiste wywołania bazy danych nigdy nie są testowane jednostkowo, to prawda. Ale to tylko kilka wierszy kodu. I to jest do przyjęcia.
źródło
Testowanie z danymi zewnętrznymi jest testem integracyjnym. Test jednostkowy oznacza, że testujesz tylko jednostkę. Najczęściej odbywa się to za pomocą logiki biznesowej. Aby umożliwić testowanie jednostki kodu, musisz przestrzegać kilku wskazówek, na przykład uniezależnić jednostkę od innych części kodu. Jeśli potrzebujesz danych jednostkowych podczas testu jednostkowego, musisz wymusić te dane z zastrzykiem zależności. Istnieją pewne ramy kpiny i karczowania.
źródło