Jak tworzyć testy jednostkowe na warstwie CRUD aplikacji, jak mogę uniezależnić testy?

14

Staram się więc, aby moje testy jednostkowe były jak najbardziej zgodne z książką, ale stają się kłopotliwe, gdy testuję proste metody dodawania / usuwania.

W przypadku metody dodawania muszę w zasadzie utworzyć fikcyjny obiekt i dodać go, a następnie po pomyślnym zakończeniu testu muszę usunąć fikcyjny obiekt.

I do testu usuwania, oczywiście muszę stworzyć fikcyjny obiekt, aby móc go usunąć.

Jak widać, jeśli jeden test się nie powiedzie, drugi też się nie powiedzie, ponieważ oba są nieco potrzebne.

Podobnie jest w przypadku systemu, w którym należy napisać test „Anuluje zamówienie” ... cóż, aby anulować w pierwszej kolejności, konieczne byłoby zamówienie na fałszywe zamówienie, czy nie jest to niezgodne z wytycznymi dotyczącymi testowania jednostkowego?

Jak mają być obsługiwane takie przypadki?

Kyle
źródło
Możesz także rzucić okiem na to pytanie: programmers.stackexchange.com/questions/115455/…
Guven

Odpowiedzi:

13

Cóż, nie ma nic złego w tym, co robisz. Wiele testów może obejmować ten sam kod; oznacza to tylko, że jeden problem spowoduje, że kilka testów zakończy się niepowodzeniem. To, czego chcesz uniknąć, to testy, które zależą od wyników innych testów. Tj. Test usuwania zależy od uruchomienia testu dodawania, a zatem jeśli uruchomisz test usuwania PRZED testem dodawania, nie powiedzie się. Aby uniknąć tego problemu, upewnij się, że masz „pustą tablicę” na początku każdego testu, aby to, co dzieje się w danym teście, nie wpłynęło na kolejne testy.

Świetnym sposobem na to jest uruchomienie testów dla bazy danych w pamięci.

W teście dodawania utwórz pustą bazę danych, dodaj obiekt i sprawdź, czy rzeczywiście został dodany.

W teście usuwania utwórz bazę danych z obiektem, który chcesz już usunąć. Usuń obiekt i potwierdź, że został usunięty.

Zdmuchnij bazę danych ze swojego rozwijanego kodu.

Adam Jaskiewicz
źródło
Baza danych w pamięci jest po prostu szybka (w pamięci) i prosta (w trakcie). Możesz to zrobić w dowolnym magazynie danych.
Paul Draper,
3

Użyj transakcji.

Jeśli korzystasz z bazy danych obsługującej transakcje, uruchom każdy test w transakcji. Wycofaj transakcję na koniec testu. Następnie każdy test pozostawi bazę danych bez zmian.

Tak, aby anulować zamówienie, najpierw musisz je utworzyć. W porządku. Test najpierw utworzy zamówienie, a następnie je anuluje, a następnie sprawdzi, czy zamówienie zostało anulowane.

Kevin Cline
źródło
Podoba mi się ten pomysł. Wdrożono go z wielkim skutkiem dzisiaj.
pimbrouwers
3

Dobrze sobie radzisz. Jedyną podstawową zasadą testowania jednostkowego jest objęcie każdej ścieżki kodu, dzięki czemu masz pewność, że Twój kod robi to, co powinien, i robi to po zmianach i refaktoryzacji. Utrzymywanie małych testów jednostkowych, prostych i jednozadaniowych jest wartościowym celem, ale nie jest fundamentalne. Posiadanie testu, który wywołuje dwie powiązane metody interfejsu API, samo w sobie nie jest wątpliwe, w rzeczywistości, jak zauważyłeś, często jest to konieczne. Wadą zbędnych testów jest to, że poświęcają więcej czasu na pisanie, ale ponieważ prawie wszystko jest w fazie rozwoju, jest to kompromis, który musisz robić cały czas, a najlepsze rozwiązanie prawie nigdy nie jest jednym z ekstremalnych punktów.

Kilian Foth
źródło
2

Techniki testowania oprogramowania są bardzo zróżnicowane, a im więcej się o nich dowiesz, zaczniesz widzieć wiele różnych (a czasem sprzecznych) wskazówek. Nie ma jednej „książki” do przejścia.

Myślę, że znajdujesz się w sytuacji, w której widziałeś wskazówki dotyczące testów jednostkowych, które mówią coś takiego

  • Każdy test powinien być samodzielny i nie powinny mieć na niego wpływu inne testy
  • Każdy test jednostkowy powinien przetestować jedną rzecz i tylko jedną rzecz
  • Testy jednostkowe nie powinny trafić do bazy danych

i tak dalej. Wszystkie są prawidłowe, w zależności od tego, jak zdefiniujesz „test jednostkowy” .

Zdefiniowałbym „test jednostkowy” jako coś w rodzaju: „test, który wykonuje jedną funkcjonalność dla jednej jednostki kodu, odizolowany od innych zależnych komponentów”.

Zgodnie z tą definicją to, co robisz (jeśli wymaga dodania rekordu do bazy danych przed uruchomieniem testu), wcale nie jest „testem jednostkowym”, ale raczej tym, co jest powszechnie nazywane „testem integracyjnym”. (Prawdziwy test jednostkowy, z mojej definicji, nie trafi do bazy danych, więc nie trzeba dodawać rekordu przed jego usunięciem).

Test integracyjny sprawdzi funkcjonalność wykorzystującą wiele komponentów (takich jak interfejs użytkownika i baza danych), a wytyczne, które miałyby zastosowanie do testów jednostkowych, niekoniecznie dotyczą testów integracyjnych.

Jak wspomnieli inni w swoich odpowiedziach, to, co robisz, niekoniecznie jest złe, nawet jeśli robisz rzeczy sprzeczne z niektórymi wskazówkami dotyczącymi testów jednostkowych. Zamiast tego spróbuj uzasadnić to, co naprawdę testujesz w każdej metodzie testowej, a jeśli okaże się, że potrzebujesz wielu komponentów, aby spełnić test, a niektóre komponenty wymagają wstępnej konfiguracji, to zrób to.

Ale przede wszystkim rozumiem, że istnieje wiele rodzajów testów oprogramowania (testy jednostkowe, testy systemowe, testy integracyjne, testy eksploracyjne itp.) I nie próbuj stosować wskazówek jednego typu do wszystkich innych.

Eric King
źródło
Czy mówisz, że nie możesz usunąć testu jednostkowego z bazy danych?
ChrisF
Jeśli trafiasz do bazy danych, jest to (z definicji) test integracji, a nie test jednostkowy. W tym sensie nie. Nie można usunąć testu jednostki z bazy danych. Czego można badanej jednostki jest to, że gdy kod testujesz jest proszony, aby usunąć niektóre dane, współdziała z modułem dostępu do danych poprawnie.
Eric King,
Ale chodzi o to, że niektórzy ludzie mogą inaczej definiować „test jednostkowy”, dlatego musimy zachować ostrożność, stosując wytyczne dotyczące „testu jednostkowego”, ponieważ wskazówki mogą nie mieć zastosowania w sposób, w jaki uważamy, że tak się dzieje.
Eric King,
1

Właśnie dlatego jedną z innych wytycznych jest używanie interfejsów. Jeśli metoda przyjmuje obiekt, który implementuje interfejs zamiast implementacji określonej klasy, można utworzyć klasę, która nie zależy od reszty podstawy kodu.

Inną alternatywą jest użycie frameworku. Umożliwiają one łatwe tworzenie tego typu obojętnych obiektów, które można przekazać do testowanej metody. Możliwe, że będziesz musiał utworzyć niektóre implementacje kodu pośredniczącego dla klasy fikcyjnej, ale nadal powoduje to oddzielenie od faktycznej implementacji i tego, czego dotyczy test.

unholysampler
źródło
1

Jak widać, jeśli jeden test się nie powiedzie, drugi też się nie powiedzie, ponieważ oba są nieco potrzebne.

Więc?

... czy to nie jest sprzeczne z wytycznymi dotyczącymi testów jednostkowych?

Nie.

Jak mają być obsługiwane takie przypadki?

Kilka testów może być niezależnych i wszystkie kończą się niepowodzeniem z powodu tego samego błędu. To właściwie normalne. Wiele testów może - pośrednio - przetestować niektóre typowe funkcje. Wszystkie zawodzą, gdy wspólna funkcjonalność jest zepsuta. Nie ma w tym nic złego.

Testy jednostkowe są precyzyjnie zdefiniowane jako klasy , dzięki czemu mogą łatwo udostępniać kod, podobnie jak wspólny rekord fikcyjny używany do testowania aktualizacji i usuwania.

S.Lott
źródło
1

Możesz użyć fałszywego frameworka lub „środowiska” z bazą danych w pamięci. Ta ostatnia to klasa, w której możesz stworzyć wszystko, czego potrzebujesz, aby test przeszedł pomyślnie, zanim test się uruchomi.

Wolę ostatni - użytkownicy mogą pomóc ci wprowadzić dane, aby Twoje testy były jak najbliższe rzeczywistemu światu.

Andre
źródło
To prawda - ale tak naprawdę nie testujesz tutaj prawdziwego połączenia z bazą danych. Chyba że zakładasz, że to zawsze zadziała - ale założenia są niebezpieczne.
ChrisF