Jak TDD zwracać prawidłowe wyniki

12

Zaczynam nowy projekt i bardzo bardzo staram się używać TDD do kierowania projektem. Naciskam od lat i wreszcie uzyskałem zgodę na poświęcenie dodatkowego czasu na ten projekt, aby go wykorzystać, podczas gdy uczę się, jak robić to poprawnie.

To jest nowy moduł do połączenia z istniejącym systemem. Obecnie dostęp do wszystkich danych odbywa się za pośrednictwem usług internetowych, które w większości stanowią jedynie cienkie opakowanie procedur przechowywanych w bazie danych.

Jednym z wymagań jest to, że dla danego sklepu zwracam wszystkie zamówienia, które są uważane za ważne dla tej aplikacji. Zamówienie zakupu uważa się za ważne, jeśli jego data wysyłki przypada w danym zakresie od daty otwarcia sklepów (dotyczy to nowych sklepów).

Teraz nie mogę umieścić tej logiki w kodzie aplikacji, ponieważ nie zamierzam przywracać miliona PO, aby uzyskać tuzin, które mogą ubiegać się o ten sklep, biorąc pod uwagę powyższe ograniczenie.

Myślałem, że mogę przekazać zakres dat do procedury GetValidPOs i użyć tych wartości do zwrócenia prawidłowych zamówień zakupu. Ale co, jeśli dodamy kolejny wymóg do tego, co jest uważane za prawidłową PO?

Jak to przetestować i sprawdzić, czy działa dalej? Nie używamy ORM i jest mało prawdopodobne. I nie mogę zadzwonić do DB w moim teście.

Utknąłem.

Inną moją myślą jest to, że niektóre udają, że zwracają prawidłowe dane, inne, które zwracają niektóre złe dane, i lokalne repozytorium zgłasza wyjątek, jeśli wystąpią złe dane, i przetestuj, czy wyjątek zostanie zgłoszony, jeśli nieprawidłowe dane zostaną zwrócone przez proc GetValidPOs (lub próbka użyta do testowania).

Czy to ma sens? Czy jest jakiś lepszy sposób?

AKTUALIZACJA: Wydaje mi się, że mogę używać EF. Teraz muszę tylko wymyślić, jak go używać i sprawić, by był testowalny, a jednocześnie nadal polegać na procedurach przechowywanych i trudnościach z rozproszeniem danych w kilku bazach danych.

CaffGeek
źródło
Z ciekawości dlaczego nie możesz wybrać tylko prawidłowych zamówień za pomocą zwykłej instrukcji SQL? (To pytanie lub odpowiedź nie sugeruje rozwiązania.)
scarfridge

Odpowiedzi:

7

Jest to główny minus procedur przechowywanych w erze TDD. Mają pewne pozytywne strony, nawet teraz, ale z definicji każdy test, który wykonuje zapisany proc, nie jest testem jednostkowym; w najlepszym razie jest to test integracyjny.

Zwykłym rozwiązaniem, zakładającym, że architektura nie może zmienić się na użycie ORM, nie jest umieszczanie tych testów w zestawie testów jednostkowych; zamiast tego umieść testy w pakiecie integracyjnym. Nadal możesz uruchomić test, ilekroć chcesz sprawdzić, czy działa, ale ponieważ koszt związany z przygotowaniem testu (zainicjowanie bazy danych z odpowiednimi danymi testowymi) jest wysoki i dotyka zasobów agenta testów jednostkowych twojego build-bota mieć dostęp do, nie powinno być w pakiecie testów jednostkowych.

Nadal możesz testować jednostkowo kod, który wymaga danych, poprzez wyodrębnienie wszystkiego, czego nie możesz przetestować jednostkowo (klasy ADO.NET), do klasy DAO, którą możesz następnie wyśmiewać. Następnie można zweryfikować, czy oczekiwane wywołania są wykonywane przez użycie kodu i odtworzenie rzeczywistych zachowań (takich jak nie znajdowanie wyników), umożliwiając testowanie różnych przypadków użycia. Jednak faktyczne skonfigurowanie SqlCommand do wywoływania przechowywanego proc jest właściwie ostatnią rzeczą, którą można przetestować jednostkowo, oddzielając tworzenie poleceń od wykonywania poleceń i wyśmiewając executora poleceń. Jeśli brzmi to jak oddzielenie obaw, może być; pamiętajcie: „nie ma problemu, którego nie da się rozwiązać za pomocą innej warstwy pośredniej, z wyjątkiem posiadania zbyt wielu warstw pośrednich”. W pewnym momencie musisz powiedzieć „dość; po prostu nie mogę tego przetestować jednostkowo, my”

Inne opcje:

  • Przetestuj przechowywany proc przy użyciu „krótkotrwałej” instancji DBMS, takiej jak SQLite. Zwykle łatwiej jest to zrobić, gdy używasz ORM, ale test można następnie wykonać „w pamięci” (lub przy użyciu wstępnie ustawionego pliku bazy danych dołączonego do pakietu testowego). Nadal nie jest to test jednostkowy, ale można go uruchomić z wysokim stopniem izolacji (DBMS jest częścią działającego procesu, a nie czymś, z czym się łączysz zdalnie, co może znajdować się w środku pakietu testowego innego użytkownika). Minusem jest to, że zmiany w przechowywanym proc mogą wystąpić w produkcji bez testu odzwierciedlającego zmianę, więc musisz być zdyscyplinowany, aby upewnić się, że najpierw wprowadzono zmianę w środowisku testowym.

  • Rozważ uaktualnienie do ORM. ORM z dostawcą Linq (praktycznie wszystkie te, które są w powszechnym użyciu, mają taki), umożliwiłby zdefiniowanie zapytania jako instrukcji Linq; taka instrukcja może być następnie przekazana fałszywemu repozytorium, które ma w pamięci kolekcję danych testowych do zastosowania. W ten sposób możesz sprawdzić, czy zapytanie jest poprawne, nawet nie dotykając DB (powinieneś nadal uruchomić zapytanie w środowisku integracyjnym, aby sprawdzić, czy dostawca Linq może poprawnie przetrawić zapytanie).

KeithS
źródło
2
-1, ponieważ TDD! = Testowanie jednostkowe. Idealnie w porządku, aby uwzględnić testy poziomu integracji podczas TDD.
Steven A. Lowe
Testowanie jednostkowe jest podzbiorem rozwoju opartego na testach. W rozwoju opartym na testach stworzyłbyś chodzący szkielet systemu, a następnie przeprowadziłbyś testy jednostkowe, integracyjne i funkcjonalne w tym systemie. Twoje testy integracyjne, jednostkowe lub akceptacyjne kończą się niepowodzeniem, a następnie zmuszasz je do zdania i napisania dalszych testów.
CodeART
1
Rozumiem to wszystko, oboje. Gdzie powiedziałem, że test integracyjny oznacza, że ​​nie możesz go TDD? Chodzi mi o to, że procedura przechowywana nie może być testowana w oderwaniu, co jest czymś, co chcesz zrobić dla jak największej liczby baz kodu. Zamiast tego testowanie SP wymaga bardziej złożonych, dłuższych testów integracyjnych; mimo że są one jeszcze lepsze niż testy ręczne, zestaw testów obciążony integracją może potrwać kilka godzin i może mieć szkodliwy wpływ na wysiłki CI.
KeithS
Testowanie SP często wymaga również określonego zestawu danych w testowej bazie danych; kod, aby uzyskać bazę danych we właściwym stanie, aby osiągnąć oczekiwane wyniki, jest bardzo często bardziej LoC i kilkakrotnie dłuższy niż kod, który faktycznie ćwiczysz. To dodatkowo pogarsza złożoność czasową zestawu testów i często konfigurację należy powtarzać dla każdego pojedynczego testu (i prawdopodobnie powinno być ich kilka dla każdego SP, aby sprawdzić, czy spełnione są wszystkie wymagania funkcjonalne zawarte w zapytaniu).
KeithS,
Procedury przechowywane mogą być testowane w izolacji. Jak inaczej byłyby zatwierdzone? Dla Transact SQL jest tSQLt ( tsqlt.org )
kevin cline
4

Radzę podzielić i podbić . Na razie zapomnij o bazie danych i trwałości i skoncentruj się na testowaniu fałszywych implementacji repozytoriów lub obiektów dostępu do danych.

Teraz nie mogę umieścić tej logiki w kodzie aplikacji, ponieważ nie zamierzam przywracać miliona PO, aby uzyskać tuzin, które mogą ubiegać się o ten sklep, biorąc pod uwagę powyższe ograniczenie.

Wyśmiewałbym repozytorium, które zwraca zamówienia. Utwórz makietę z dwudziestoma dziwnymi zamówieniami zakupu.

Myślałem, że mogę przekazać zakres dat do procedury GetValidPOs i użyć tych wartości do zwrócenia prawidłowych zamówień zakupu. Ale co, jeśli dodamy kolejny wymóg do tego, co jest uważane za prawidłową PO?

Stub połączenie do GetValidPOs, aby wywoływał twoją próbkę, a nie procedurę bazy danych.

Jak to przetestować i sprawdzić, czy działa dalej? Nie używamy ORM i jest mało prawdopodobne. I nie mogę zadzwonić do DB w moim teście.

Potrzebujesz testu jednostkowego, aby upewnić się, że poprawne dane są zwracane z próbnego.

Potrzebujesz również testu integracji, aby upewnić się, że poprawne dane są zwracane z bazy danych. Test integracji wymagałby pewnej konfiguracji i czyszczenia. Na przykład przed uruchomieniem testu integracji należy zaszczepić bazę danych, uruchamiając skrypt. Sprawdź, czy skrypt działał. Zapytaj bazę danych, wywołując procedury składowane. Sprawdź, czy wyniki są poprawne. Oczyść bazę danych.

Inną moją myślą jest to, że niektóre udają, że zwracają prawidłowe dane, inne, które zwracają niektóre złe dane, i lokalne repozytorium zgłasza wyjątek, jeśli wystąpią złe dane, i przetestuj, czy wyjątek zostanie zgłoszony, jeśli nieprawidłowe dane zostaną zwrócone przez proc GetValidPOs (lub próbka użyta do testowania).

Jak już powiedziałem, potrzebujesz makiety, która zwraca przynajmniej niektóre dane, które możesz zapytać.

Podczas wyszukiwania danych chcesz mieć pewność, że Twój system z łatwością obsłuży wyjątki. Dlatego kpisz sobie z zachowania, aby generowało wyjątki w niektórych scenariuszach. Następnie piszesz testy, aby upewnić się, że Twój system może z powodzeniem obsługiwać te wyjątki.

CodeART
źródło
Właśnie to próbuję zrobić. Trudności z pisaniem prawdziwej implementacji, która będzie działać tak samo, jak próbka, ponieważ nasz dostęp do danych nie sprzyja korzystaniu z ORM. Większość danych, których potrzebuję, znajduje się w kilku systemach i powinna być dostępna za pośrednictwem usług internetowych ... nawet podczas aktualizacji.
CaffGeek
0

Tak jak testowanie jednostkowe Java lub JavaScript oznacza pisanie testów jednostkowych przy użyciu języka Java dla java, a testowanie jednostkowe funkcji Javascript za pomocą Javascript, pisanie automatycznych testów, które doprowadzą Cię do napisania procedur przechowywanych, oznacza, że ​​biblioteka testów jednostkowych, której szukasz, jest oparta na przechowywanej procedury.

Innym sposobem jest użycie procedur przechowywanych do przetestowania procedur przechowywanych, ponieważ:

  • ponieważ rozwijasz się w języku procedury, powinieneś mieć umiejętność pisania testów w języku procedury
  • pisanie testów w języku procedury zwiększy Twoje umiejętności w języku procedury, co z kolei pomoże w rozwoju produktu
  • będziesz mieć bezpośredni dostęp do wszystkich narzędzi udostępnianych przez twoją bazę danych. Możesz także z nich korzystać, aby testy jednostkowe były jak najprostsze
  • testy jednostkowe przechowywane w tej samej bazie danych co procedury, które testują będą szybkie (rzecz najbliższa testowi jednostkowemu, np. prędkości), ponieważ nie przekraczasz granic systemu

Podobnie jak TDD w języku OO, chcesz, aby test jednostkowy skonfigurował tylko wiersz danych, aby przetestować to, czego potrzebuje do procedury (minimalizm, tylko to, czego potrzebują twoje proste testy). W rezultacie będziesz mieć kilka prostych testów jednostkowych dla każdej procedury składowanej. Te proste testy będą łatwiejsze w utrzymaniu niż skomplikowane testy, które zależą od dużego zestawu danych, który nie łatwo odwzorowuje z powrotem na to, czego faktycznie potrzebuje test.

Rodzaj Lance'a
źródło