TDD - Outside In vs Inside Out

53

Jaka jest różnica między budowaniem aplikacji Outside In a budowaniem aplikacji Inside Out za pomocą TDD?

Oto książki, które czytałem o TDD i testowaniu jednostkowym:
Rozwój oparty na testach: Przez przykład
Rozwój oparty na testach: Praktyczny przewodnik: Praktyczny przewodnik
Realne rozwiązania dla rozwoju wysokiej jakości ram i aplikacji PHP
Rozwój oparty na testach w Microsoft.
Wzorce testowe NET xUnit: Refaktoryzacja kodu testowego
Sztuka testowania jednostkowego: z przykładami w .Net
Uprojektowane oprogramowanie obiektowe, prowadzone przez testy ---> To było naprawdę trudne do zrozumienia, ponieważ JAVA nie jest moim podstawowym językiem :)

Prawie wszyscy wyjaśnili podstawy TDD i testy jednostkowe w ogóle, ale bez wzmianki o różnych sposobach budowy aplikacji.

Zauważyłem też, że większość tych książek (jeśli nie wszystkie) ignoruje fazę projektowania podczas pisania aplikacji. Koncentrują się bardziej na szybkim pisaniu przypadków testowych i pozostawianiu projektu samodzielnie.

Jednak natknąłem się na paragraf we wzorcach testowych xUnit, który omawiał sposób, w jaki ludzie podchodzą do TDD. Istnieją 2 szkoły Outside In vs Inside Out .

Niestety książka nie wyjaśnia więcej na ten temat. Chciałbym wiedzieć, jaka jest główna różnica między tymi 2 przypadkami.
Kiedy powinienem użyć każdego z nich?
Który z początkujących TDD jest łatwiejszy do zrozumienia?
Jakie są wady każdej metody?
Czy są jakieś materiały, które konkretnie omawiają ten temat?

Songo
źródło
Te dwa podejścia opisano w witrynie wzorców testowych XUnit : xunitpatterns.com/Philosophy%20Of%20Test%20Automation.html . To dziwne, że nie ma ich w książce.
guillaume31,

Odpowiedzi:

45

Inside-Out i Outside-In to dość rzadkie terminy, częściej słyszałem / czytałem o szkole klasycznej i londyńskiej .

  • Inside-Out (klasyczna szkoła, oddolne ): zaczynasz na poziomie części / klasy (wewnątrz) i dodajesz testy do wymagań. W miarę ewolucji kodu (z powodu refaktoryzacji) pojawiają się nowi współpracownicy, interakcje i inne komponenty. TDD całkowicie kieruje projektem.

  • Outside-In (londyńska szkoła, top-down lub „mockist TDD”, jak nazwałby to Martin Fowler): wiesz o interakcjach i współpracownikach z góry (szczególnie tych na najwyższych poziomach) i zaczynasz tam (najwyższy poziom), kpiąc z niezbędnych zależności. Z każdym ukończonym komponentem przechodzisz do poprzednio wyśmiewanych współpracowników i ponownie zaczynasz od TDD, tworząc rzeczywiste implementacje (które mimo że były używane, nie były wcześniej potrzebne dzięki abstrakcjom ). Należy pamiętać, że poza-in podejście pasuje YAGNI zasady.

Żadne z tych podejść nie jest jedyne ; oboje mają swoje miejsce w zależności od tego, co robisz. W dużych rozwiązaniach dla przedsiębiorstw, w których części projektu pochodzą od architektów (lub istnieją z góry), można zacząć od „londyńskiego stylu”. Z drugiej strony, gdy masz do czynienia z sytuacją, w której nie masz pewności, jak powinien wyglądać Twój kod (lub jak powinien on pasować do innych części systemu), łatwiej jest zacząć od komponentu niższej klasy i pozwolić mu ewoluują wraz z wprowadzaniem kolejnych testów, refaktoryzacji i wymagań.

Czegokolwiek używasz, najczęściej jest to sytuacyjne.

Do dalszej lektury jest post grupy Google z dość interesującą dyskusją na temat tego, jak powstało to rozróżnienie i dlaczego Londyn może nie być najbardziej odpowiednią nazwą.

km
źródło
2
Ciekawy. Jak doszedłeś do wniosku, że na zewnątrz w TDD jest TDD „mockist”? Bardzo wolę myślenie i projektowanie na zewnątrz, a tym samym testowanie (patrz softwareonastring.com/2015/01/10/... ), ale artykuł Fowlera zdecydowanie umieszcza mnie z Fowlerem w obozie klasycystycznym. Chociaż mockist może zawsze stosować podejście zewnętrzne, nie można go odwrócić i powiedzieć, że projektowanie i testowanie zewnętrzne to mockist TDD. Na zewnątrz może być i jest bardzo praktykowane przez klasycystów TDD.
Marjan Venema
@jimmy_keen - Czy na zewnątrz w którymkolwiek momencie zastępujesz kpiny w testach wyższego poziomu nowymi, rzeczywistymi implementacjami? A może pozostawiasz je jako fałszywe zależności, a następnie ćwiczysz cały kod produkcyjny jako test integracyjny?
wyjący
1
Nie zgadzam się, że Classic / Mockist i Inside-Out / Outside-In są powiązane. Są ortogonalne. Możesz użyć Inside-Out / Outside-In z dowolnym z nich.
Daniel Kaplan
Zgadzam się z Danielem. Porównujesz dwie taksonomie, które są różne. Chociaż rozwój zewnętrzny jest często kojarzony ze szkołą licealną (mockist), nie zawsze tak jest.
guillaume31,
Nie sądzę, żeby to był poprawny opis procesu zewnętrznego. Chodzi o testowanie z publicznych interfejsów, bez sprzęgania z wewnętrznymi, w jak największym stopniu.
mcintyre321
15

Krótka odpowiedź: jak zwykle będzie to zależeć od preferencji programistycznych i podejścia zespołowego.

Kodowanie na lewą stronę jest świetne, ponieważ zawsze coś działa. Minusem jest to, że niekoniecznie pomaga dostać się do radykalnie innego miejsca. Trudniej jest tak wytyczyć kurs. Podobnie, pisanie kodu na zewnątrz ma tę wadę, że niekoniecznie korzysta z szybkiego iteracyjnego rozwoju i niekoniecznie widzi wszystkie możliwości i wzorce, które mogą wynikać z głębokiej struktury kodu.

Doszedłem do wniosku, że oba style rozwoju są ważne i że w rzeczywistości pomocne jest mieszanie stylów w zespole. Chodzi o to, że wywrotka jest świetna do tworzenia elementów konstrukcyjnych, a myślenie na zewnątrz zapewnia strukturę formy i kierunek.

Część mojego rozumowania pochodzi z bardzo popularnej szkoły myślenia, która obecnie promuje iteracyjny rozwój, który często jest równoznaczny z rozwojem na wylot. Uważam, że iteracyjny rozwój jest świetny, gdy nie masz zbyt daleko. Uważam jednak, że myślenie całościowe, w przeciwieństwie do procesu czysto iteracyjnego, jest nieocenione dla niektórych rodzajów innowacji i dotarcia do mniej oczywistego miejsca. Właściwie zarządzane, wewnątrz i na zewnątrz razem mogą być bardzo skutecznym połączeniem.

EL Yusubov
źródło
8

Do tej listy należy dodać zwinne zasady, wzorce i praktyki w języku C # . Nie wiem, dlaczego wybrał „in C #” na końcu. Książki wcale nie są językiem, a jedynym powodem, dla którego nie zdobył 5 gwiazdek w Amazon, są ludzie, którzy byli rozczarowani C # -ness jego przykładów.

Autor opowiada się za tym, aby w miarę możliwości próbować pisać kod na zewnątrz i polegać w dużej mierze na projektach ewolucyjnych, i zgadzam się z jego stwierdzeniem. Jego rozumowanie jest takie, że gdy dodamy funkcjonalność, nasz projekt zawsze będzie ewoluował. Jeśli zaczniemy od komponentów niskiego poziomu w miarę dodawania funkcji, zdamy sobie sprawę, że te komponenty nie robią tego, co chcielibyśmy, lub że trzeba je przenosić. Może to stać się dość drogie, zwłaszcza jeśli za każdym razem, gdy przenosisz funkcjonalność z jednej klasy do drugiej, musisz wykonać to samo przejście we wszystkich projektach testów jednostkowych.

Z drugiej strony, jeśli w pierwszej kolejności określisz, co powinna zrobić Twoja aplikacja, kodujesz interfejs zewnętrzny. W miarę dodawania funkcji i powiększania się testowanego kodu zmieniamy aplikację na większą liczbę klas, ale mimo tego, że trwają prace nad refaktoryzacją, oryginalne testy jednostkowe, które napisałeś, pozostają ważne. Zaczynasz więc całkowicie na zewnątrz i kontynuujesz refaktoryzację do coraz większej liczby klas niskiego poziomu, jednocześnie dodając dodatkowe testy jednostkowe do tych klas wewnętrznych, ale rzadko będziesz musiał się poruszać i przepisywać testy jednostkowe.

Jeśli jednak zidentyfikujesz konkretny podsystem niskiego poziomu, którego będzie potrzebować Twoja aplikacja (a być może Twoja firma już potrzebuje takiego podsystemu w innych aplikacjach), nadszedł czas, aby najpierw zacząć od niskiego poziomu bloku konstrukcyjnego, a następnie zbuduj aplikację.

DXM
źródło
7

Moim zdaniem koncepcja rozwoju zewnętrznego naprawdę rozprzestrzenia się na 2 poziomy. Gerard Meszaros krótko opisuje je jako „ projektowanie zewnętrzne ” i „ kodowanie zewnętrzne / wewnętrzne ”.

  • Pierwszy poziom to poziom organizacyjny i procesowy. Projekt na zewnątrz ma na celu przeciwieństwo odgórnego (wodospad / taylorist) i oddolnego. Dzięki podejściu zewnętrznemu skupiamy się na perspektywie użytkownika końcowego. Zaczynamy od testów historycznych, testów ATDD lub BDD i przechodzimy do „wewnętrznych” wniosków technicznych testów i kodu. Projektowanie zewnętrzne jest więc tym, co można zrobić w kontekście zwinnym. Dan North świetnie mówi o podejściach BDD, odgórnych, oddolnych i zewnętrznych.

  • Drugi poziom jest techniczny i dotyczy warstw aplikacyjnych. Kodowanie zewnętrzne oznacza w zasadzie rozpoczynanie od interfejsu użytkownika i przechodzenie do warstwy centralnej (zwykle warstwy biznesowej / domeny). W przeciwieństwie do kodowania na lewą stronę, który zaczyna się od warstwy środkowej, a koduje warstwy zewnętrzne na końcu.

Możesz więc mieć projekt zewnętrzny z kodowaniem zewnętrznym lub wewnętrznym.

Nie zgadzam się z Meszarosem, gdy kojarzy kodowanie od wewnątrz z testami integracji, argumentując, że w kontekście od wewnątrz „nie testujemy zewnętrznego oprogramowania w oderwaniu od oprogramowania wewnętrznego”. Ale wierzę, że nic nie stoi na przeszkodzie, aby to zrobić. Możesz doskonale wybrać testowanie obiektów warstwy zewnętrznej, wyśmiewając obiekty warstwy wewnętrznej, nawet jeśli kod produkcyjny dla tych obiektów już istnieje. Musisz po prostu dodać interfejsy i kpiny na istniejących obiektach betonowych zamiast pisać interfejsy, kpić z nich, a następnie tworzyć implementacje później, jak w przypadku kodowania zewnętrznego.

Innymi słowy, mockistowski lub klasycystyczny TDD to IMO, ortogonalna troska o kodowanie wewnątrz / na zewnątrz. Możesz doskonale zastosować styl mockist wraz z podejściem odwróconym na lewą stronę. Powodem tego jest to, że styl mockist / klasycyzm dotyczy zależności kodu, podczas gdy kodowanie na zewnątrz / na zewnątrz dotyczy warstw aplikacyjnych .

Inną ważną rzeczą jest to, że zależności występują nie tylko między warstwami, ale również między obiektami na tej samej warstwie. Na przykład możesz zacząć od jednego obiektu w centralnej warstwie biznesowej (podejście odwrócone) i użyć próbnych elementów, aby odizolować obiekt od innych obiektów warstwy biznesowej, z którymi rozmawia. Dzieje się tak często w przypadku IoC - abstrakcje, od których zależy Twój obiekt, są często deklarowane w tej samej warstwie, ale konkretne implementacje są w innej warstwie.

Robert „Wujek Bob” Martin w swoim poście „ Czysta architektura ” krótko wspomina o kodowaniu od wewnątrz i jak niekoniecznie koliduje on z architekturą oddzieloną od architektury .

guillaume31
źródło