2008-09-28 10 views
263

Pracuję z wieloma aplikacjami internetowymi, które są obsługiwane przez bazy danych o różnym stopniu złożoności w zapleczu. Zazwyczaj istnieje warstwa ORM oddzielona od logiki biznesowej i prezentacji. To sprawia, że ​​testowanie jednostkowe logiki biznesowej jest dość proste; rzeczy mogą być zaimplementowane w oddzielnych modułach, a wszelkie dane potrzebne do testu można sfałszować poprzez kpiny z obiektów.Jaka jest najlepsza strategia testowania jednostkowego aplikacji bazujących na bazach danych?

Jednak testowanie ORM i samej bazy danych zawsze było obciążone problemami i kompromisami.

Przez te wszystkie lata próbowałem kilku strategii, z których żadna nie całkowicie mnie usatysfakcjonowała.

  • Załaduj testową bazę danych ze znanymi danymi. Przeprowadź testy przeciwko ORM i sprawdź, czy wracają właściwe dane. Wadą jest to, że Twój testowy DB musi nadążać za zmianami schematu w bazie danych aplikacji i może się zsynchronizować. Opiera się również na sztucznych danych i nie może ujawniać błędów, które pojawiają się z powodu głupich danych wprowadzanych przez użytkownika. Wreszcie, jeśli testowa baza danych jest mała, nie ujawni ona nieefektywności, takich jak brakujący indeks. (OK, ten ostatni nie jest tak naprawdę testem jednostkowym, ale nie zaszkodzi.)

  • Załaduj kopię produkcyjnej bazy danych i przetestuj ją. Problem polega na tym, że nie masz pojęcia, co znajduje się w DB produkcji w danym momencie; Twoje testy mogą wymagać ponownego napisania, jeśli dane zmieniają się w czasie.

Niektóre osoby wskazały, że obie te strategie opierają się na konkretnych danych, a test jednostkowy powinien testować tylko funkcjonalność. W tym celu widziałem zasugerowane:

  • Użyj próbnego serwera bazy danych i sprawdź tylko, czy ORM wysyła prawidłowe zapytania w odpowiedzi na dane wywołanie metody.

Jakie strategie zastosowano do testowania aplikacji bazujących na bazach danych? Co zadziałało najlepiej dla ciebie?

Odpowiedz

116

ja rzeczywiście wykorzystane swoje pierwsze podejście z jakiegoś sukcesu, ale w nieco inny sposób, który myślę, że będzie rozwiązać niektóre problemy:

  1. Trzymać cały schemat i skryptów do tworzenia go w kontrola źródła, dzięki czemu każdy może utworzyć aktualny schemat bazy danych po wyrejestrowaniu. Ponadto zachowaj przykładowe dane w plikach danych, które zostaną załadowane przez część procesu kompilacji. Gdy odkryjesz dane, które powodują błędy, dodaj je do przykładowych danych, aby sprawdzić, czy błędy nie pojawiają się ponownie.

  2. Użyj ciągłego serwera integracyjnego do zbudowania schematu bazy danych, załaduj przykładowe dane i uruchom testy. W ten sposób utrzymujemy synchronizację testowej bazy danych (przebudowujemy ją przy każdym uruchomieniu testowym). Chociaż wymaga to, aby serwer CI miał dostęp i własność własnej dedykowanej instancji bazy danych, mówię, że posiadanie naszego schematu db budowanego 3 razy dziennie znacznie pomogło znaleźć błędy, które prawdopodobnie nie zostałyby znalezione przed samym dostarczeniem (jeśli nie później). Nie mogę powiedzieć, że odbudowuję schemat przed każdym zatwierdzeniem. Czy ktoś? Dzięki takiemu podejściu nie będziesz musiał (może powinniśmy, ale nie jest to wielka sprawa, jeśli ktoś zapomni).

  3. Dla mojej grupy wprowadzanie użytkownika odbywa się na poziomie aplikacji (nie db), więc jest to testowane za pomocą standardowych testów jednostkowych.

Ładowanie produkcyjnej bazy danych Kopiuj:
Było to podejście, które zostało wykorzystane w mojej ostatniej pracy. To był ogromny ból przyczyną kilku problemów:

  1. Kopia dostanie nieaktualna od wersji produkcyjnej
  2. Zmiany zostaną wykonane do schematu kopia, a nie dostanie propagowane do systemów produkcyjnych . W tym momencie mielibyśmy rozbieżne schematy. Nie śmieszne.

Prześmiewanie serwer bazy danych:
Robimy to również w mojej obecnej pracy. Po każdym zatwierdzeniu przeprowadzamy testy jednostkowe w odniesieniu do kodu aplikacji, do których wprowadzono fałszywe dodatki db. Następnie trzy razy dziennie wykonujemy pełną kompilację db opisaną powyżej. Zdecydowanie polecam oba podejścia.

+20

Ładowanie kopii bazy danych produkcyjnych ma również wpływ na bezpieczeństwo i prywatność. Kiedy zrobi się duży, zrobienie kopii i umieszczenie go w środowisku programisty może być wielką sprawą. –

+0

Szczerze, to jest ogromny ból. Jestem nowy w testowaniu i napisałem też orm, który chcę przetestować. Użyłem już twojej pierwszej metody, ale przeczytaj, że nie robi ona jednostki testowej.Używam określonej funkcjonalności silnika db, więc kpiny z DAO będą trudne. Myślę, że źle po prostu używam mojej obecnej metody, ponieważ działa, a inni jej używają. Zautomatyzowane testy rock btw. Dzięki. – frostymarvelous

+2

Zarządzam dwoma różnymi dużymi projektami, w jednym z nich to podejście było idealne, ale mieliśmy wiele problemów próbując wdrożyć to w innym projekcie. Myślę więc, że to zależy od tego, jak łatwo można odtworzyć schemat za każdym razem, aby wykonać testy. Obecnie pracuję nad znalezieniem nowego rozwiązania tego ostatniego problemu. – Cross

11

Zadaję to pytanie od dłuższego czasu, ale myślę, że nie ma na to srebrnej kuli.

To, co obecnie robię, to wyśmiewanie obiektów DAO i przechowywanie reprezentacji w pamięci dobrego zbioru obiektów reprezentujących interesujące przypadki danych, które mogą mieszkać w bazie danych.

Głównym problemem, jaki widzę z tym podejściem, jest to, że obejmuje on tylko kod, który współdziała z twoją warstwą DAO, ale nigdy nie testuje samego DAO i na podstawie mojego doświadczenia widzę, że na tej warstwie występuje wiele błędów także. Prowadzę również kilka testów jednostkowych, które działają w bazie danych (ze względu na używanie TDD lub szybkie testowanie lokalnie), ale te testy nigdy nie są uruchamiane na moim serwerze ciągłej integracji, ponieważ nie przechowujemy bazy danych do tego celu, a ja uważam, że testy uruchamiane na serwerze CI powinny być niezależne.

Innym podejściem, które uważam za bardzo interesujące, ale nie zawsze warte uwagi, jest nieco czasochłonne, polega na utworzeniu tego samego schematu, którego używa się do produkcji we wbudowanej bazie danych, która działa tylko w ramach testowania urządzenia.

Nawet jeśli nie ma wątpliwości, to podejście poprawia zasięg, istnieje kilka wad, ponieważ musisz być jak najbardziej zbliżony do ANSI SQL, aby działał zarówno z bieżącym DBMS i zamiennikiem osadzonym.

Bez względu na to, co Twoim zdaniem jest bardziej istotne dla Twojego kodu, istnieje kilka projektów, które mogą ułatwić, na przykład DbUnit.

46

Ja zawsze uruchamiania testów przeciwko DB w pamięci (HSQLDB lub Derby) dla tych powodów:

  • To sprawia, że ​​myślisz, jakie dane zachować w DB testowej i dlaczego. Przeniesienie bazy produkcyjnej do systemu testowego przekłada się na "Nie mam pojęcia, co robię i dlaczego, a jeśli coś się zepsuje, to nie ja!" ;)
  • Zapewnia, że ​​bazę danych można odtworzyć z niewielkim wysiłkiem w nowym miejscu (na przykład, gdy musimy replikować błąd z produkcji).
  • Pomaga ogromnie z jakością plików DDL.

Baza danych w pamięci jest ładowana świeżymi danymi po rozpoczęciu testów i po większości testów, wzywam ROLLBACK, aby zachować stabilność. ZAWSZE utrzymuj dane w testowym DB stabilnym! Jeśli dane zmieniają się cały czas, nie można testować.

Dane są ładowane z SQL, szablonu DB lub zrzutu/kopii zapasowej. Preferuję zrzuty, jeśli są w czytelnym formacie, ponieważ mogę umieścić je w VCS. Jeśli to nie zadziała, używam pliku CSV lub XML. Jeśli muszę załadować ogromne ilości danych ... Nie mam. Nigdy nie musisz ładować ogromnych ilości danych :) Nie dla testów jednostkowych. Testy wydajności to kolejna kwestia i obowiązują inne zasady.

+1

Czy prędkość jest jedynym powodem używania (w szczególności) DB? – rinogo

+1

Przypuszczam, że kolejną zaletą może być jego "wyrzucona" natura - nie ma potrzeby sprzątania po sobie; po prostu zabij DB w pamięci. (Są jednak inne sposoby, aby to osiągnąć, takie jak wspomniane podejście ROLLBACK). – rinogo

+1

Zaletą jest to, że każdy test może indywidualnie wybrać swoją strategię. Mamy testy, które wykonują pracę w wątkach potomnych, co oznacza, że ​​Spring zawsze będzie zatwierdzać dane. –

3

Używam pierwszego (uruchamiając kod względem testowej bazy danych). Jedynym istotnym problemem, jaki widzę, że podnosisz z takim podejściem, jest możliwość zsynchronizowania schematów, którymi się zajmuję, utrzymując numer wersji w mojej bazie danych i wprowadzając wszystkie zmiany schematu za pomocą skryptu, który stosuje zmiany dla każdego przyrostu wersji.

Wszystkie zmiany (w tym również do schematu bazy danych) najpierw wprowadzam w moim środowisku testowym, więc kończy się to na odwrót: Po przejściu wszystkich testów zastosuj aktualizacje schematu na hoście produkcyjnym. Posiadam również osobną parę baz testowych w porównaniu do baz danych aplikacji w moim systemie programistycznym, dzięki czemu mogę sprawdzić, czy aktualizacja db działa poprawnie przed dotknięciem prawdziwego pola produkcyjnego.

8

Nawet jeśli istnieją narzędzia, które pozwalają na mock bazy danych w taki czy inny sposób (np jOOQ „s MockConnection, które można zobaczyć w this answer - zrzeczenie się, pracuję dla dostawcy jOOQ za), radziłbym nie fałszować większe bazy ze złożonymi zapytaniami.

Nawet jeśli po prostu chcesz do integracji-przetestować ORM, strzeżcie się, że ORM wydaje bardzo złożoną serię zapytań do bazy danych, które mogą różnić się w

  • składni
  • złożoność
  • zamówienie (!)

Kpiny z tego wszystkiego do produkcji rozsądnych danych fikcyjnych są dość trudne, chyba że faktycznie budujesz małą bazę danych wewnątrz twojej sztuczki, która interpretuje przesyłane statuetki SQL nts. Powiedziawszy to, użyj dobrze znanej bazy danych testów integracji, którą możesz łatwo zresetować przy użyciu dobrze znanych danych, na podstawie których możesz przeprowadzić testy integracji.

0

W przypadku projektu opartego na JDBC (bezpośrednio lub pośrednio, np. JPA, EJB, ...) nie można utworzyć makiety całej bazy danych (w takim przypadku lepiej byłoby użyć bazy testowej na prawdziwym systemie RDBMS), ale tylko makieta na poziomie JDBC.

Zaletą jest abstrakcja, która jest dostarczana w ten sposób, ponieważ dane JDBC (zestaw wyników, liczba aktualizacji, ostrzeżenie, ...) są takie same, jak jest to backend: prod db, testowa db lub tylko niektóre dane makiet przewidziane dla każdego przypadku testowego.

Dzięki połączeniu JDBC do każdego przypadku nie ma potrzeby zarządzania testową db (oczyszczanie, tylko jeden test w czasie, ponowne ładowanie urządzeń, ...). Każde połączenie makiety jest izolowane i nie ma potrzeby czyszczenia. W każdym przypadku testowym dostępne są tylko minimalne wymagane urządzenia do makiety wymiany JDBC, które pomagają uniknąć złożoności zarządzania całą bazą testową.

Struktura Acolyte zawiera sterownik JDBC i narzędzie do tego typu makiety: http://acolyte.eu.org.

Powiązane problemy