Jakie są szkoły TDD w Londynie i Chicago?

88

Słyszałem o stylu londyńskim vs. stylu chicagowskim (czasem nazywanym stylem Detroit) Test Driven Development (TDD).

Warsztaty grupy użytkowników programowania w Utah Extreme:

TDD w stylu interakcji jest również nazywane mockistowskim lub londyńskim stylem po londyńskim klubie Extreme Tuesday, gdzie stał się popularny. Zwykle kontrastuje z TDD w stylu Detroit lub klasycznym, który jest bardziej oparty na stanie.

Warsztat Jasona Gormana :

Warsztaty obejmują zarówno chicagowską szkołę TDD (państwowe testy zachowania i triangulację), jak i londyńską szkołę , która koncentruje się bardziej na testowaniu interakcji, kpinach i kompleksowym TDD, ze szczególnym uwzględnieniem projektowania opartego na odpowiedzialności oraz Powiedz, nie pytaj, podejście do OO ostatnio ponownie spopularyzowane przez doskonałą książkę Steve Freeman i Nat Pryce Growing Object-Oriented Software Guided By Tests .

Post Classic TDD czy „London School”? Jason Gorman był pomocny, ale jego przykłady mnie zdezorientowały, ponieważ używa dwóch różnych przykładów zamiast jednego z obu podejść. Jakie są różnice? Kiedy używasz każdego stylu?

Arturo Herrero
źródło

Odpowiedzi:

76

Załóżmy, że masz klasę o nazwie „ledger”, metodę o nazwie „kalkuluj”, która korzysta z „kalkulatora” do wykonywania różnych rodzajów obliczeń w zależności od argumentów przekazanych do „obliczania”, na przykład „pomnóż (x, y)” lub „odejmij ( x, y) ”.

Załóżmy teraz, że chcesz sprawdzić, co się stanie, gdy wywołasz ledger.calculate („5 * 7”).

Szkoła London / Interaction poprosiłaby cię o sprawdzenie, czy zadzwonił Calculator.multiply (5,7). Różne frameworki są przydatne do tego i może być bardzo przydatne, jeśli na przykład nie masz własności obiektu „Kalkulator” (załóżmy, że jest to zewnętrzny komponent lub usługa, której nie można przetestować bezpośrednio, ale robisz to wiedz, że musisz zadzwonić w określony sposób).

W szkole w Chicago / stanie chciałbyś, abyś uzyskał wynik 35. Ramy jUnit / nUnit są na ogół nastawione na to.

Oba są ważnymi i ważnymi testami.

Matthew Flynn
źródło
Bardzo fajny przykład.
sevenseacat
1
Dodam jeszcze kilka powodów użycia każdego z nich: Jeśli ważną rzeczą jest ustalenie, że coś się zmieniło lub nie zmieniło się na podstawie podjętej akcji (np. Ledger.bucket.value staje się 35, gdy ledger.calculate („5 * 7 ”)), chcesz użyć twierdzeń stanu (szkoła chicagowska). Jest to najbardziej pomocne, gdy masz pełną kontrolę nad stanem systemu przed wywołaniem metody i kiedy faktycznie kontrolujesz, co robi metoda.
Matthew Flynn,
1
Jeśli ważne jest, aby wiedzieć, że wywoływana jest druga metoda (np. Calculator.multiply (5, 7)), należy użyć asercji działania, jak za pośrednictwem próbnego obiektu. Jest to najbardziej pomocne, jeśli wywoływana metoda ma pożądany efekt uboczny (np. Zapisywanie danych, zwiększanie licznika, wysyłanie wiadomości itp.), Jeśli tak naprawdę nie kontrolujesz, co robi metoda, więc zwracana wartość może być niespójna . Ponadto, jeśli nie możesz łatwo kontrolować stanu systemu, najlepiej możesz określić, jakie działania mają miejsce.
Matthew Flynn,
Podejście londyńskie jest przydatne, gdy klasa Kalkulator z jakiegoś powodu jest potencjalnie długa, lub obejmuje sieć, więc może być niestabilna w konfiguracjach dev / qa. Tj. Kpina pozwala twoim testom być szybka i niezawodna w przypadkach, w których inaczej nie byłoby to możliwe.
Kevin
1
Podejście londyńskie również zapewnia wyraźniejsze sygnały zwrotne, ponieważ jeśli nastąpi Calculatorregresja multiply, zobaczysz dwa testy zakończone niepowodzeniem: test księgi i test kalkulatora, ale tylko jeden test zakończy się niepowodzeniem, jeśli wyśmiejesz kalkulator. To może ułatwić ustalenie źródła błędu, szczególnie jeśli system jest złożony.
Matthias
30

Artykuł Mocks Aren't Stubs autorstwa Martina Fowlera stanowi dobre wprowadzenie do tematu.

W zależności od wybranego stylu projektowania (i zasad projektowania, na podstawie których budujesz swoje programy) istnieją co najmniej dwa sposoby widzenia obiektu:

  1. Jako jednostka wykonująca obliczenia na podstawie danych wejściowych. W wyniku tego obliczenia obiekt może zwrócić wartość lub zmienić swój stan.
  2. Jako aktywny element komunikujący się z innymi elementami w systemie poprzez przekazywanie wiadomości.

W pierwszym przypadku jesteś zainteresowany tym, co wyjdzie z przetwarzania lub w jakim stanie obiekt zostanie pozostawiony po tym przetwarzaniu. To tutaj metody, takie jak assertEquals()wprowadzenie obrazu. W tym przypadku nie ma większego znaczenia, jakie inne obiekty były zaangażowane w przetwarzanie, jakie metody zostały wywołane itp. Ten rodzaj weryfikacji nazywany jest weryfikacją opartą na stanie i jest stylem „klasycznym”.

W drugim przypadku, ponieważ większość obiektów nawet nie zwraca żadnego wyniku (np. voidMetody w Javie), jesteś bardziej zainteresowany tym, jak obiekty komunikują się ze sobą i czy przekazują odpowiednie wiadomości w okolicznościach narzuconych przez test. Te interakcje są zwykle weryfikowane za pomocą fałszywych ram. Ten rodzaj weryfikacji nazywany jest weryfikacją opartą na zachowaniu lub interakcją. Jednym z jej implikacji jest technika zwana Behaviour Driven Development, dzięki której rozwijasz klasę, zakładając, że jej współpracownicy już istnieją (nawet jeśli jeszcze nie istnieją), więc możesz kodować na ich interfejsach.

Zauważ, że nie jest to żaden wybór. Możesz mieć styl projektowania, który łączy oba podejścia, aby jak najlepiej wykorzystać każde z nich.

Otavio Macedo
źródło