W swoim wystąpieniu TDD, gdzie wszystko poszło nie tak , Ian Cooper popiera pierwotną intencję Kenta Becka za testowaniem jednostkowym w TDD (testowanie zachowań, a nie metod klas) i argumentuje za unikaniem łączenia testów z implementacją.
W przypadku zachowania takiego jak save X to some data source
w systemie z typowym zestawem usług i repozytoriów, w jaki sposób możemy testować jednostkowo zapisywanie niektórych danych na poziomie usługi za pośrednictwem repozytorium, bez powiązania testu ze szczegółami implementacji (np. Wywołanie określonej metody )? Czy unikanie tego rodzaju sprzężenia nie jest w jakiś sposób warte wysiłku / zła?
unit-testing
tdd
coupling
Andy Hunt
źródło
źródło
Odpowiedzi:
Twój konkretny przykład to przypadek, w którym zwykle musisz przetestować, sprawdzając, czy dana metoda została wywołana, ponieważ
saving X to data source
oznacza komunikowanie się z zewnętrzną zależnością , więc zachowanie, które musisz przetestować, polega na tym, że komunikacja odbywa się zgodnie z oczekiwaniami .Nie jest to jednak nic złego. Interfejsy graniczne między aplikacją a jej zewnętrznymi zależnościami nie są szczegółami implementacji , w rzeczywistości są zdefiniowane w architekturze systemu; co oznacza, że taka granica najprawdopodobniej się nie zmieni (lub jeśli będzie to konieczne, będzie to najmniej częsta zmiana). Tak więc sprzężenie testów z
repository
interfejsem nie powinno przysparzać Ci zbyt wiele problemów (jeśli tak, rozważ, czy interfejs nie kradnie obowiązków aplikacji).Teraz rozważ tylko reguły biznesowe aplikacji oddzielonej od interfejsu użytkownika, baz danych i innych usług zewnętrznych. Jest tak, jeśli musisz mieć swobodę zmiany zarówno struktury, jak i zachowania kodu. W tym miejscu testy sprzęgania i szczegóły implementacji wymuszą zmianę kodu testowego więcej niż kodu produkcyjnego, nawet jeśli nie nastąpi zmiana w ogólnym zachowaniu aplikacji. W tym miejscu testy
State
zamiastInteraction
pomagać nam iść szybciej.PS: Nie zamierzam mówić, czy testowanie według stanu lub interakcji jest jedynym prawdziwym sposobem TDD - uważam, że jest to kwestia użycia odpowiedniego narzędzia do właściwej pracy.
źródło
Moja interpretacja tego wystąpienia jest następująca:
Nie jest to określone w wykładzie, ale myślę, że założony kontekst porady jest podobny:
Zatem testowanie komponentu jest największym możliwym zakresem, w którym coś nadal można rozsądnie nazwać testowaniem jednostkowym. Różni się to raczej od tego, jak niektórzy ludzie, zwłaszcza akademicy, używają tego terminu. Nie przypomina to przykładów w typowym samouczku do testów jednostkowych. Jednak odpowiada to swojemu pochodzeniu podczas testowania sprzętu; płyty i moduły są testowane jednostkowo, a nie przewody i śruby. A przynajmniej nie budujesz fałszywego Boeinga do testowania śruby ...
Wyciągając z tego ekstrapolację i wtrącając własne myśli,
Jeśli zrobisz to właściwie i czysto, ledwo potrzebujesz narzędzia kpiącego; zużywa się tylko kilka razy na system.
Baza danych jest zazwyczaj współpracownikiem, więc zostaje sfałszowana, a nie wyśmiewana. Wykonanie tego byłoby bolesne ręcznie; na szczęście takie rzeczy już istnieją .
Podstawowym wzorcem testowym jest wykonanie sekwencji operacji (np. Zapisanie i ponowne załadowanie dokumentu); potwierdź, że działa. Jest to to samo, co w przypadku każdego innego scenariusza testowego; żadna (działająca) zmiana implementacji prawdopodobnie nie spowoduje niepowodzenia takiego testu.
Wyjątkiem są zapisy bazy danych, które nigdy nie są odczytywane przez testowany system; np. dzienniki kontroli lub podobne. Są to dane wyjściowe i dlatego powinny być wyśmiewane. Wzorzec testowy wykonuje pewną sekwencję operacji; potwierdź, że interfejs kontroli został wywołany przy użyciu metod i argumentów określonych.
Zauważ, że nawet tutaj, pod warunkiem, że używasz bezpiecznego narzędzia do kpin typu mockito , zmiana nazwy metody interfejsu nie może spowodować niepowodzenia testu. Jeśli użyjesz IDE z załadowanymi testami, zostanie ono refaktoryzowane wraz ze zmianą nazwy metody. Jeśli nie, test nie zostanie skompilowany.
źródło
Moją sugestią jest zastosowanie podejścia testowego opartego na stanie:
DANE Mamy testową bazę danych DB w znanym stanie
KIEDY Usługa jest wywoływana z argumentami X
NASTĘPNIE Potwierdź, że baza danych zmieniła się ze stanu pierwotnego do stanu oczekiwanego, wywołując metody repozytorium tylko do odczytu i sprawdzając ich zwrócone wartości
W ten sposób nie polegasz na żadnym wewnętrznym algorytmie usługi i możesz dowolnie refaktoryzować jej implementację bez konieczności zmiany testów.
Jedynym sprzężeniem jest tutaj wywołanie metody usługi i wywołania repozytorium potrzebne do odczytania danych z bazy danych, co jest w porządku.
źródło