Właśnie przeczytałem fragment książki „Growing Object-Oriented Software”, która wyjaśnia kilka powodów, dla których kpiny z konkretnej klasy nie są zalecane.
Oto przykładowy kod testu jednostkowego dla klasy MusicCentre:
public class MusicCentreTest {
@Test public void startsCdPlayerAtTimeRequested() {
final MutableTime scheduledTime = new MutableTime();
CdPlayer player = new CdPlayer() {
@Override
public void scheduleToStartAt(Time startTime) {
scheduledTime.set(startTime);
}
}
MusicCentre centre = new MusicCentre(player);
centre.startMediaAt(LATER);
assertEquals(LATER, scheduledTime.get());
}
}
I jego pierwsze wyjaśnienie:
Problem z tym podejściem polega na tym, że pozostawia domniemany związek między przedmiotami. Mam nadzieję, że do tej pory wyjaśniliśmy, że zamiarem rozwoju opartego na testach z Mock Objects jest odkrywanie relacji między obiektami. Jeśli podklasę, w kodzie domeny nie ma nic, co by uwidoczniło taką relację, tylko metody na obiekcie. Utrudnia to sprawdzenie, czy usługa obsługująca tę relację może być istotna w innym miejscu, i będę musiał ponownie przeprowadzić analizę następnym razem, gdy będę pracować z klasą.
Nie mogę dokładnie zrozumieć, co on ma na myśli, mówiąc:
Utrudnia to sprawdzenie, czy usługa obsługująca tę relację może być istotna w innym miejscu, i będę musiał ponownie przeprowadzić analizę następnym razem, gdy będę pracować z klasą.
Rozumiem, że usługa odpowiada MusicCentre
wywołanej metodzie startMediaAt
.
Co rozumie przez „gdzie indziej”?
Pełny fragment znajduje się tutaj: http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html
Odpowiedzi:
Autor tego postu promuje używanie interfejsów zamiast klas członków.
It turns out that my MusicCentre object only uses the starting and stopping methods on the CdPlayer, the rest are used by some other part of the system. I'm over-specifying my MediaCentre by requiring it to talk to a CdPlayer, what it actually needs is a ScheduledDevice.
Relacją, którą martwi się o ponowne odkrycie później, jest fakt, że klasa MediaCentre nie potrzebuje całego obiektu CdPlayer. Twierdzi, że używając interfejsu (przypuszczalnie ograniczonego do uruchomienia | stop), łatwiej jest zrozumieć, czym tak naprawdę jest interakcja.
„gdzie indziej” oznacza po prostu, że inne obiekty mogą mieć podobnie ograniczone relacje, a pełny obiekt składowy nie jest wymagany - podzbiór funkcjonalności zawinięty w interfejsie powinien być wystarczający.
Roszczenie zaczyna mieć sens, gdy eksplodujesz wszystkie potencjalne funkcje:
Teraz jego twierdzenie „Po prostu potrzebuję rozpocząć i zatrzymać” ma więcej sensu. Użycie konkretnego elementu członkowskiego zamiast interfejsu sprawia, że przyszłym deweloperom nie jest jasne, co jest naprawdę wymagane. Przeprowadzanie testów jednostkowych z MediaCentre na wszystkich innych funkcjach w CdPlayer jest marnotrawstwem wysiłku testowego, ponieważ należą one do stanu „nie przejmuj się”. Jeśli
Record
funkcja nie działała w tym przypadku, naprawdę nas to nie obchodzi, ponieważ nie jest wymagana. Ale przyszły opiekun niekoniecznie wiedziałby o tym na podstawie napisanego kodu.Ostatecznie założeniem autora jest wykorzystanie tylko tego, co jest potrzebne i wyjaśnienie przyszłym opiekunom tego, co było wcześniej wymagane. Celem jest zminimalizowanie przeróbek / ponownej analizy modułu kodu podczas późniejszej konserwacji.
źródło
Po zastanowieniu się nad tym, otrzymuję możliwą interpretację tego cytatu:
Cytowana „usługa” odpowiada „faktowi planowania”. Może to być wyrażone przez dobrze nazwany i „skoncentrowany na jednej roli” interfejs o nazwie „ScheduledDevice” lub wyrażone pośrednio przez konkretną implementację metody niezależną od jakichkolwiek interfejsów.
W powyższym przykładzie harmonogram jest wyrażony przez cały obiekt o pełnej nazwie
CDPlayer
. W ten sposób nadal prowadzi do domniemanego związku międzyMusicCentre
„faktem planowania”.Więc jeśli zaczniemy wstrzykiwać konkretne klasy i wyśmiewać je do obiektów wysokiego poziomu; kiedy chcemy je przetestować, musimy przeanalizować każdy wstrzyknięty „konkretny” obiekt, aby zobaczyć, czy przedstawia on konkretną zależność, którą MUSIMY PRZECZYTAĆ, ponieważ są UKRYTE (niejawne). Wręcz przeciwnie, kodowanie ZAWSZE nad interfejsem pozwala programistom bezpośrednio dowiedzieć się, jaki rodzaj relacji ma być obsługiwany przez obiekt wysokiego poziomu, a tym samym wykryć funkcje, które należy wyśmiewać, aby wyodrębnić test jednostkowy.
źródło
Miałem tu na myśli usługę CDPlayer.scheduleToStartAt (). Tak nazywa się MediaCentre - współpracownik, którego potrzebuje, aby funkcjonować. MediaCentre jest testowanym obiektem.
Chodzi o to, że jeśli jasno wyrażę tylko to, od czego zależy MediaCentre, a nie klasę implementacji, mogę nadać tej roli zależności i mówić o niej. MediaCentre musi tylko wiedzieć, że rozmawia z urządzeniami ScheduledDevices. Gdy reszta systemu się zmieni, nie będę musiał zmieniać MediaCentre, chyba że zmienią się jego funkcje.
To pomaga?
źródło