Nie rozumiem, w jaki sposób TDD pomaga mi uzyskać dobry projekt, jeśli potrzebuję projektu, aby go przetestować

50

Staram się owinąć głowę wokół TDD, a konkretnie części programistycznej. Przeglądałem niektóre książki, ale te, które znalazłem, dotyczą głównie części testowej - Historii NUnit, dlaczego testowanie jest dobre, Red / Green / Refactor i jak stworzyć kalkulator strun.

Dobre rzeczy, ale to „tylko” testy jednostkowe, a nie TDD. W szczególności nie rozumiem, w jaki sposób TDD pomaga mi uzyskać dobry projekt, jeśli potrzebuję go, aby rozpocząć testowanie.

Aby to zilustrować, wyobraź sobie następujące 3 wymagania:

  • Katalog musi zawierać listę produktów
  • Katalog powinien pamiętać, które produkty oglądał użytkownik
  • Użytkownicy powinni mieć możliwość wyszukiwania produktu

W tym momencie wiele książek wyciąga magicznego królika z kapelusza i po prostu zagłębia się w „Testowanie usługi produktu”, ale nie wyjaśniają, w jaki sposób doszli do wniosku, że istnieje usługa produktu. To jest część „Rozwój” w TDD, którą próbuję zrozumieć.

Musi istnieć istniejący projekt, ale nigdzie nie można znaleźć rzeczy poza usługami encji (to znaczy: istnieje Produkt, więc powinna istnieć Usługa Produktu) (np. Drugi wymóg wymaga ode mnie pewnej koncepcji Użytkownik, ale gdzie mam umieścić funkcję przypominania? Czy funkcja wyszukiwania jest funkcją usługi ProductService lub osobnej usługi wyszukiwania? Skąd mam wiedzieć, którą wybrać?)

Według SOLID potrzebowałbym UserService, ale jeśli zaprojektuję system bez TDD, mogę skończyć z całą gamą usług Single-Method Services. Czy TDD nie ma przede wszystkim skłonić mnie do odkrycia mojego projektu?

Jestem programistą .net, ale zasoby Java również by działały. Wydaje mi się, że nie ma prawdziwej przykładowej aplikacji ani książki, która dotyczyłaby prawdziwej aplikacji biznesowej. Czy ktoś może podać jasny przykład ilustrujący proces tworzenia projektu przy użyciu TDD?

Michael Stum
źródło
2
TDD jest tylko częścią całej metodologii rozwoju. Oczywiście będziesz musiał zastosować jakiś projekt (z góry lub lepiej ewolucyjny), aby zebrać całość.
Euforia
3
@gnat: Pytanie, dlaczego książki TDD nie wyjaśniają procesu projektowania.
Robert Harvey
4
@gnat: To była twoja edycja, nie moja. :) Zobacz moją zmianę tytułu pytania i treści.
Robert Harvey
9
Jeśli przeczytałeś pracę Roberta C. Martina, a może obejrzałeś jeden z jego filmów, zobaczysz, że często ma on na myśli projekt, ale nie jest z nim związany. On jest przekonany , że jego pre-poczęte pojęcie właściwej konstrukcji wyjdzie z jego badań, ale nie zmuszać go. I w końcu czasami ten projekt ma, a czasem nie. Chodzi mi o to, że twoje wcześniejsze doświadczenia poprowadzą cię, ale testy powinny cię poprowadzić. Testy powinny być w stanie opracować lub obalić twój projekt.
Anthony Pegram
3
Więc tak naprawdę nie chodzi o testowanie, ale o projektowanie. Tyle, że tak naprawdę nie pomaga ci to w projektowaniu, a także pomaga w sprawdzeniu poprawności projektu. Ale czy to nie jest! @ # $ Ing testowanie?
Erik Reppen

Odpowiedzi:

17

Ideą TDD jest zacząć od testowania i od tego pracować. Dlatego przykład „Katalog musi mieć listę produktów” może być postrzegany jako test „Sprawdź produkty w katalogu”, a zatem jest to pierwszy test. Co zawiera katalog? Co zawiera produkt? To są kolejne elementy, a pomysł polega na zebraniu kilku części, które byłyby czymś w rodzaju usługi ProductService, która narodzi się po przejściu pierwszego testu.

Ideą TDD jest rozpoczęcie od testu, a następnie napisanie kodu, który sprawia, że ​​test ten przechodzi jako pierwszy punkt. Testy jednostkowe są częścią tego, tak, ale nie patrzysz na ogólny obraz, który powstaje, zaczynając od testów, a następnie pisząc kod, aby w tym momencie nie było martwych punktów, ponieważ nie ma jeszcze żadnego kodu.


Test Driven Development Tutorial, w którym slajdy 20-22 są kluczowe. Chodzi o to, aby wiedzieć, co powinna zrobić funkcja, napisać test, a następnie zbudować rozwiązanie. Część projektu będzie się różnić, ponieważ w zależności od tego, co jest wymagane, może być lub nie być tak prosta do zrobienia. Kluczową kwestią jest zastosowanie TDD od samego początku, a nie próba wprowadzenia go późno w projekt. Jeśli najpierw zaczniesz od testów, może to pomóc i prawdopodobnie warto w pewnym sensie zauważyć. Jeśli spróbujesz dodać testy później, staje się to czymś, co można odłożyć lub opóźnić. Przydatne mogą być także późniejsze slajdy.


Główną zaletą TDD jest to, że rozpoczynając od testów, nie jesteś początkowo związany z projektem. Dlatego chodzi o zbudowanie testów i stworzenie kodu, który przejdzie te testy jako metodologię programowania. Big Projekt Up przednia może powodować problemy, jak to daje się pomysł blokowania rzeczy na miejsce, które sprawia, że system jest zbudowany, aby być mniej zwinny w końcu.


Robert Harvey dodał to w komentarzach, które warto zaznaczyć w odpowiedzi:

Niestety myślę, że jest to powszechne nieporozumienie na temat TDD: nie można rozwijać architektury oprogramowania, po prostu pisząc testy jednostkowe i umożliwiając ich zaliczenie. Pisanie testów jednostkowych ma wpływ na projekt, ale nie tworzy projektu. Musisz to zrobić.

JB King
źródło
31
@MichaelStum: Niestety myślę, że jest to powszechne nieporozumienie na temat TDD: nie można opracować architektury oprogramowania, pisząc testy jednostkowe i umożliwiając ich zaliczenie. Pisanie testów jednostkowych ma wpływ na projekt, ale nie tworzy projektu. Musisz to zrobić.
Robert Harvey
4
@RobertHarvey, JimmyHoffa: gdybym mógł głosować twoje komentarze 100 razy, zrobiłbym to!
Doc Brown
9
@Robert Harvey: Cieszę się, że napisałeś o tym powszechnym nieporozumieniu: zbyt często słyszę, że trzeba tylko usiąść i napisać wszelkiego rodzaju testy jednostkowe, a projekt „wyłoni się” spontanicznie. A jeśli twój projekt jest zły, to dlatego, że nie napisałeś wystarczającej liczby testów jednostkowych. Zgadzam się z tobą, że testy są narzędziem do określania i weryfikowania wymagań dla twojego projektu, ale „musisz to zrobić” sam. W pełni się zgadzam.
Giorgio
4
@Giorgo, RobertHarvey: +1000 do Roberta Harvey również ode mnie. Niestety, to nieporozumienie jest na tyle powszechne, że niektórzy „eksperci” praktykujący TDD / Agile uważają, że to prawda. Podobnie jak na przykład, udają, że można „ewoluować” solver sudoku z TDD, bez wiedzy o domenie ani analizy jakiegokolwiek rodzaju . Zastanawiam się, czy Ron Jeffries opublikował kiedyś śledztwo w sprawie ograniczeń TDD lub wyjaśnił, dlaczego nagle przerwał eksperyment bez wyciągania wniosków i wniosków.
Andres F.,
3
@Andres F: Znam historię sudoku i myślę, że jest bardzo interesująca. Myślę, że niektórzy programiści popełniają błąd, myśląc, że narzędzie (np. TDD lub SCRUM) może zastąpić wiedzę domenową i własne wysiłki, i oczekuję, że poprzez mechaniczne zastosowanie konkretnej metody dobre oprogramowanie magicznie się „pojawi”. Często są to ludzie, którzy nie lubią spędzać zbyt dużo czasu na analizie i projektowaniu i wolą kodować coś bezpośrednio. Dla nich przestrzeganie określonej metodologii jest alibi na to, aby nie robić właściwego projektu. Ale to jest IMHO niewłaściwe użycie TDD.
Giorgio
8

Za to, co jest warte, TDD pomaga mi szybciej znaleźć najlepszą konstrukcję niż nie robić TDD. Prawdopodobnie wybrałbym najlepszy projekt z nim lub bez niego. Ale ten czas, który spędziłbym na przemyśleniu i zrobieniu kilku ciosów w kodzie, spędziłem zamiast tego na pisaniu testów. I to mniej czasu. Dla mnie. Nie dla każdego. I nawet jeśli zajęłoby to tyle samo czasu, dałoby mi to zestaw testów, dzięki czemu refaktoryzacja byłaby bezpieczniejsza, co prowadziłoby do jeszcze lepszego kodu na końcu.

Jak to robi?

Po pierwsze, zachęca mnie do myślenia o każdej klasie jako o usłudze dla jakiegoś kodu klienta. Lepszy kod wynika z myślenia o tym, jak kod wywołujący chce korzystać z interfejsu API, zamiast martwienia się o to, jak powinien on wyglądać.

Po drugie, powstrzymuje mnie to od pisania zbyt dużej złożoności cyklometrycznej w jednej metodzie, podczas gdy ja o tym myślę. Każda dodatkowa ścieżka prowadząca do metody może podwoić liczbę testów, które muszę wykonać. Zwykłe lenistwo dyktuje, że po dodaniu zbyt dużej logiki i muszę napisać 16 testów, aby dodać jeden warunek, nadszedł czas, aby wyciągnąć część z nich do innej metody / klasy i przetestować osobno.

To naprawdę takie proste. To nie jest magiczne narzędzie do projektowania.

pdr
źródło
6

Próbuję owinąć głowę wokół TDD ... Aby to zilustrować, wyobraź sobie następujące 3 wymagania:

  • Katalog musi zawierać listę produktów
  • Katalog powinien pamiętać, które produkty oglądał użytkownik

Wymagania te należy przekształcić w kategoriach ludzkich. Kto chce wiedzieć, które produkty użytkownik oglądał wcześniej? Użytkownik? Sprzedawca?

  • Użytkownicy powinni mieć możliwość wyszukiwania produktu

W jaki sposób? Wg nazwy? Według marki? Pierwszym krokiem w rozwoju opartym na testach jest zdefiniowanie testu, na przykład:

browse to http://ourcompany.com
enter "cookie" in the product search box
page should show "chocolate-chip cookies" and "oatmeal cookies"

>

W tym momencie wiele książek wyciąga magicznego królika z kapelusza i po prostu zagłębia się w „Testowanie usługi produktu”, ale nie wyjaśniają, w jaki sposób doszli do wniosku, że istnieje usługa produktu.

Jeśli są to jedyne wymagania, z pewnością nie zrobiłbym skoku, aby utworzyć usługę ProductService. Mogę stworzyć bardzo prostą stronę internetową ze statyczną listą produktów. To działałoby idealnie, dopóki nie przejdziesz do wymagań dodawania i usuwania produktów. W tym momencie mogę zdecydować, że najłatwiej jest użyć relacyjnej bazy danych i ORM i utworzyć klasę produktu odwzorowaną na pojedynczą tabelę. Nadal nie ma usługi produktu. Klasy takie jak ProductService zostaną utworzone, kiedy i jeśli będą potrzebne. Może być wiele żądań internetowych, które wymagają wykonania tych samych zapytań lub aktualizacji. Następnie zostanie utworzona klasa ProductService, aby zapobiec duplikowaniu kodu.

Podsumowując, TDD steruje kodem, który ma zostać napisany. Projektowanie odbywa się podczas dokonywania wyborów implementacyjnych, a następnie przekodowywania kodu na klasy w celu wyeliminowania powielania i kontrolowania zależności. Podczas dodawania kodu musisz utworzyć nowe klasy, aby zachować kod SOLID. Ale nie musisz z góry decydować, że będziesz potrzebować klasy Product i ProductService. Może się okazać, że życie jest w porządku z klasą produktu.

Kevin Cline
źródło
OK nie ProductService. Ale w jaki sposób TDD powiedziało ci, że potrzebujesz bazy danych i ORM?
Robert Harvey
4
@Robert: tak nie było. To decyzja projektowa oparta na mojej ocenie najbardziej skutecznego sposobu spełnienia tego wymagania. Ale decyzja może się zmienić.
kevin cline
1
Dobry projekt nigdy nie będzie produkowany jako efekt uboczny jakiegoś arbitralnego procesu. Posiadanie systemu lub modelu do pracy i tworzenia ramek jest świetne, ale pierwsza wersja TDO, IMO, uderza w konflikt interesów, sprzedając się również jako coś, co zagwarantuje, że ludzie nie zostaną nieoczekiwanie ukąszeni przez skutki uboczne złego kod, który nie powinien był mieć miejsca. Projektowanie wymaga refleksji, świadomości i przemyślenia. Nie uczysz się tego poprzez przycinanie automatycznie wykrytych objawów z drzewa. Nauczysz się ich, najpierw zastanawiając się, jak unikać gałęzi zmutowanych mutantów.
Erik Reppen
Myślę, że test „dodaj produkt; uruchom ponownie komputer i uruchom ponownie system; dodany produkt powinien być nadal widoczny. ” pokazuje, skąd bierze się potrzeba jakiegoś rodzaju bazy danych (ale wciąż może to być płaski plik lub XML).
yatima2975,
3

Inni mogą się nie zgadzać, ale dla mnie wiele nowszych metodologii opiera się na założeniu, że programista zrobi większość tego, co starsze metodologie wyraziły z przyzwyczajenia lub osobistej dumy, że programista zwykle robi coś, co jest dość oczywiste dla nich, a praca jest zamknięta w czystym języku lub czystszych częściach nieco niechlujnego języka, abyś mógł wykonać całą działalność testową.

Kilka przykładów, w których natknąłem się na to w przeszłości:

  • Weź grupę wykonawców specjalizacji i powiedz im, że ich zespół jest zwinny i najpierw testuje. Często nie mają innego zwyczaju, niż pracować zgodnie ze specyfikacją i nie martwią się o jakość pracy, o ile trwa ona wystarczająco długo, aby zakończyć projekt.

  • Najpierw spróbuj zrobić coś nowego. Spędź dużo czasu na zrywaniu testów, gdy odkryjesz, że różne podejścia i interfejsy to bzdury.

  • Koduj coś na niskim poziomie i albo daj się ponieść klęskom z powodu braku zasięgu, albo napisz wiele testów, które nie mają tak dużej wartości, ponieważ nie możesz kpić z podstawowych zachowań, z którymi jesteś związany.

  • Każda sytuacja, w której nie masz wystarczająco dużo podstawowej mechaniki na miejscu, aby dodać testowalną funkcję bez uprzedniego zapisania wiązki podstawowych niestabilnych bitów, takich jak podsystem dyskowy lub interfejs komunikacyjny na poziomie tcpip.

Jeśli robisz TDD i to działa na twoje, dobre dla ciebie, ale jest wiele rzeczy (całe prace lub etapy projektu) tam, gdzie to po prostu nie dodaje wartości.

Twój przykład brzmi, jakbyś nie był jeszcze w fazie projektowania, więc albo musisz porozmawiać o architekturze, albo jesteś prototypem. Moim zdaniem musisz najpierw przejść przez niektóre z nich.

Rachunek
źródło
1

Jestem przekonany, że TDD jest bardzo cennym podejściem do szczegółowego projektu systemu - tj. Interfejsów API i modelu obiektowego. Aby jednak dojść do sedna projektu, w którym zacząłbyś używać TDD, musisz mieć duży obraz projektu, który został już w jakiś sposób modelowany i musisz mieć duży obraz architektury, który został już w jakiś sposób modelowany. @ user414076 parafrazuje Roberta Martina jako pomysł na projekt, ale nie będący z nim małżeństwem. Dokładnie. Wniosek - TDD nie jest jedynym działaniem projektowym, jest to sposób, w jaki dopracowywane są szczegóły projektu. TDD musi być poprzedzone innymi działaniami projektowymi i pasować do ogólnego podejścia (takiego jak Agile), które dotyczy sposobu tworzenia i ewolucji całego projektu.

FYI - dwie książki, które polecam na ten temat, które dają namacalne i realistyczne przykłady:

Rosnące oprogramowanie obiektowe oparte na testach - wyjaśnia i podaje pełny przykład projektu. To książka o projektowaniu, a nie testowaniu . Testowanie służy do określania oczekiwanego zachowania podczas działań projektowych.

testowanie rozwoju Praktyczny przewodnik - powolny krok po kroku tworzenie kompletnej, choć niewielkiej aplikacji.

Chuck Krutsinger
źródło
0

Wykrywanie projektu napędów TTD przez niepowodzenie testu, a nie sukces, dlatego możesz testować nieznane i iteracyjnie ponownie testować, ponieważ nieznane są ostatecznie ujawnione, co ostatecznie prowadzi do kompletnego zestawu testów jednostkowych - bardzo miło jest mieć ciągłą konserwację i bardzo trudno jest spróbować modernizacja po napisaniu / wydaniu kodu.

Na przykład może być wymagane, aby dane wejściowe mogły być w kilku różnych formatach, ale nie wszystkie są jeszcze znane. Za pomocą TDD najpierw napiszesz test, który weryfikuje, czy odpowiednie dane wyjściowe są dostarczane w dowolnym formacie wejściowym. Oczywiście ten test się nie powiedzie, więc piszesz kod do obsługi znanych formatów i ponownie testujesz. Ponieważ nieznane formaty są ujawniane poprzez zbieranie wymagań, nowe testy są pisane przed napisaniem kodu, one również powinny zakończyć się niepowodzeniem. Następnie zapisywany jest nowy kod do obsługi nowych formatów, a wszystkie testy są uruchamiane ponownie, co zmniejsza szansę na regresję.

Pomocne jest również, aby pomyśleć o awarii jednostki jako „niedokończonym” kodzie zamiast „zepsutym” kodzie. TDD pozwala na niedokończone jednostki (oczekiwane awarie), ale zmniejsza występowanie uszkodzonych jednostek (nieoczekiwane awarie).

Daniel Pereira
źródło
1
Zgadzam się, że jest to prawidłowy przepływ pracy, ale tak naprawdę nie wyjaśnia, w jaki sposób architektura wysokiego poziomu może wyjść z takiego przepływu pracy.
Robert Harvey
1
Tak, architektura wysokiego poziomu, taka jak wzorzec MVC, nie wyjdzie z samego TDD. Ale z TDD może wyłonić się kod zaprojektowany z myślą o łatwym testowaniu, co samo w sobie jest kwestią projektową.
Daniel Pereira
0

W pytaniu jest powiedziane:

... wiele książek wyciąga magicznego królika z kapelusza i po prostu zagłębia się w „Testowanie usługi produktu”, ale nie wyjaśniają, w jaki sposób doszli do wniosku, że istnieje usługa produktu.

Doszli do tego wniosku, myśląc o tym, jak zamierzają przetestować ten produkt. „Jaki to produkt?” „Cóż, moglibyśmy stworzyć usługę”. „Ok, napiszmy test dla takiej usługi”

Bryan Oakley
źródło
0

Funkcjonalność może mieć wiele projektów, a TDD nie powie ci całkowicie, która z nich jest najlepsza. Nawet jeśli testy pomogą Ci zbudować bardziej modułowy kod, może również doprowadzić do zbudowania modułów, które będą odpowiadały wymaganiom testów, a nie rzeczywistości produkcyjnej. Musisz więc zrozumieć, dokąd zmierzasz i jak wszystko powinno pasować do całego obrazu. Innymi słowy, istnieją wymagania funkcjonalne i niefunkcjonalne, nie zapomnij o ostatnim.

Odnośnie projektowania nawiązuję do książek Roberta C. Martina (Agile Development), ale także Wzorów architektury aplikacji korporacyjnych Martina Fowlera i projektowania sterowników domen. Szczególnie późniejsze jest bardzo systematyczne w wydobywaniu Istot i Relacji z wymagań.

Następnie, gdy dobrze zrozumiesz dostępne opcje, jak zarządzać tymi podmiotami, możesz nakarmić cię podejściem TDD.

Ludovic
źródło
0

Czy TDD nie ma przede wszystkim skłonić mnie do odkrycia mojego projektu?

Nie.

Jak możesz przetestować coś, czego nie zaprojektowałeś wcześniej?

Aby to zilustrować, wyobraź sobie następujące 3 wymagania:

  • Katalog musi zawierać listę produktów
  • Katalog powinien pamiętać, które produkty oglądał użytkownik
  • Użytkownicy powinni mieć możliwość wyszukiwania produktu

To nie są wymagania, to są definicje danych. Nie wiem, jaki jest cel twojego oprogramowania, ale jest mało prawdopodobne, że analitycy mówią w ten sposób.

Musisz wiedzieć, jakie są niezmienniki twojego systemu.

Wymaganiem byłoby coś takiego:

  • Klient może zamówić produkt o określonej ilości, jeśli ilość tego produktu jest wystarczająca.

Więc jeśli jest to jedyny wymóg, możesz mieć taką klasę:

public class Product {

  private int quantity;

  public Product(int initialQuantity) {
    this.quantity = initialQuantity;
  }

  public void order(int quantity) {
    // To be implemented.
  }

}

Następnie za pomocą TDD napiszesz przypadek testowy przed implementacją metody order ().

public void ProductTest() {

    public void testCorrectOrder() {

        Product p = new Product(10);
        p.order(3);
        p.order(4);

    }

    @Expect(ProductOutOfStockException)
    public void testIncorrectOrder() {

        Product p = new Product(10);
        p.order(7);
        p.order(4);

    }

}

Drugi test zakończy się niepowodzeniem, a następnie możesz zaimplementować metodę order () w dowolny sposób.

Guillaume
źródło
0

Masz całkowitą rację TDD spowoduje dobrą implementację danego projektu. To nie pomoże w procesie projektowania.

James Anderson
źródło
Daje to jednak siatkę bezpieczeństwa w celu ulepszenia projektu bez łamania działającego kodu. Jest to refaktoryzacja, którą większość ludzi pomija.
Adrian Schneider,
-3

TDD bardzo pomaga, jednak ważną rolę odgrywa tworzenie oprogramowania. Deweloper powinien słuchać pisanego kodu . Refaktoryzacja jest trzecią częścią cyklu TDD. Jest to główny krok, na którym deweloper powinien się skupić i pomyśleć przed przejściem do następnego testu czerwonego. Czy jest jakieś powielanie? Czy stosowane są zasady SOLID? Co z wysoką kohezją i niskim sprzężeniem? Co z imionami? Przyjrzyj się kodowi wyłaniającemu się z testów i sprawdź, czy jest coś, co należy zmienić, przeprojektować. Pytanie kodu i kodu powie ci, jak chce być zaprojektowany. Zwykle piszę zestawy wielu testów, sprawdzam tę listę i tworzę pierwszy prosty projekt, nie musi to być „ostateczny”, zwykle nie jest, ponieważ zmienia się przy dodawaniu nowych testów. Tam właśnie pojawia się projekt.

Adroniusz
źródło