2011-11-02 7 views
5

Przeczytałem mnóstwo artykułów, widziałem mnóstwo screencastów na temat TDD, ale wciąż mam problemy z używaniem go w rzeczywistym projekcie. Moim głównym problemem jest to, że nie wiem od czego zacząć, jaki test powinien być pierwszy. Załóżmy, że muszę napisać bibliotekę klienta wywołującą metody systemu zewnętrznego (np. Powiadomienie). Chcę to klient do pracy w następujący sposóbJak wybrać punkt początkowy TDD w realnym projekcie?

NotificationClient client = new NotificationClient("abcd1234"); // client ID 
Response code = client.notifyOnEvent(Event.LIMIT_REACHED, 100); // some params of call 

Istnieją pewne tłumaczenie i przygotowanie Format wiadomość za kulisami, więc chciałbym, aby ukryć go z moich aplikacji klienckich.

Nie wiem, gdzie i jak zacząć. Czy powinienem wymyślić jakieś surowe klasy ustawione dla tej biblioteki? powinienem zacząć od testowania NotificationClient jak poniżej

public void testClientSendInvalidEventCommand() { 
    NotificationClient client = new NotificationClient(...); 
    Response code = client.notifyOnEvent(Event.WRONG_EVENT); 
    assertEquals(1223, code.codeValue()); 
} 

Jeśli tak, to z takim teście jestem zmuszony napisać pełną realizację pracy od razu, bez raczkujemy jak stany TDD. Mogę wyśmiewać sosmething w Clientie, ale potem muszę wiedzieć, że to jest kpina z góry, więc potrzebuję jakiegoś wstępnego projektu.

Może powinienem zacząć od początku, najpierw przetestować ten komponent formatowania wiadomości, a następnie użyć go we właściwym teście klienta?

Jaką drogę wybrać? Czy zawsze powinniśmy zaczynać od początku (jak radzić sobie z tym ogromnym krokiem)? Czy możemy zacząć od dowolnej klasy realizującej malutką część pożądanej funkcji (jak w tym przykładzie Formatter)?

Gdybym wiedział, gdzie uderzyć z moimi testami, byłoby mi znacznie łatwiej przejść dalej.

Odpowiedz

1

Nie mylić acceptance tests tego haka na każdym końcu aplikacji i utworzyć executable specifications z unit tests.

Jeśli wykonujesz "czysty" TDD, piszesz test akceptacji, który steruje testami jednostkowymi, które napędzają implementację. testClientSendInvalidEventCommand to Twój test akceptacji, ale w zależności od tego, jak skomplikowane rzeczy będziesz delegował implementację do wielu klas, możesz testować osobno.

To, jak skomplikowane rzeczy się dzieją, zanim je rozdzielisz, aby je przetestować i właściwie je zrozumieć, nazywa się Test Driven Design.

+0

Ale jak znaleźć te klasy, aby pisać testy jednostek i od czego zacząć? Przypuszczam, że muszę mieć ustawione pewne klasy, aby móc wybrać punkt startowy dla TDD. Ale gdzie powinien być ten punkt? Czy powinien znajdować się na krawędziach systemu, czy może znajdować się w środku systemu - jak wspomniany jest Formatter? –

+0

Możesz TDD od dołu do góry lub z góry na dół. Nie widzę, jak formatter pasuje do twojego przykładu. Biorąc pod uwagę test, powinieneś napisać ** minimum ** ilości kodu niezbędnego do wydania. –

0

Jednym z celów TDD jest to, że testy informują o projekcie. Zatem dobrze jest pomyśleć o tym, jak wdrożyć swój NotificationClient; zmusza do myślenia (miejmy nadzieję) o prostych abstrakcjach z góry.

Ponadto, rodzaj TDD zakłada stały refaktoryzacji. Twoje pierwsze rozwiązanie prawdopodobnie nie będzie ostatnim; aby udoskonalić swój kod, testy zawierają informacje o przerwach, od błędów kompilacji po rzeczywiste problemy w środowisku wykonawczym.

Chciałbym po prostu wskoczyć i zacząć od testu, który zasugerowałeś. Podczas tworzenia makiet musisz utworzyć testy dla rzeczywistych implementacji tego, co kpisz. Przekonasz się, że rzeczy mają sens i wymagają refaktoryzacji, więc będziesz musiał modyfikować swoje testy w trakcie podróży. Tak właśnie powinno działać ...

+0

OK, więc załóżmy, że zidentyfikowałem dwóch współpracowników: Formatter do tworzenia wiadomości w wymaganym formacie oraz ConnectionHandler do obsługi połączeń i wysyłania wiadomości niskiego poziomu. Czy wystarczy rozpocząć testowanie? Od czego zacząć? Z testowaniem ConnectionHandler lub Formatter? A może Klient z tymi dwiema kancelariami wyśmiał? –

+0

Po prostu wybierz jedną lub drugą - myślę, że jesteś w paraliżu analitycznym - po prostu zrób to i będzie działać sam ... – hvgotcodes

1

Możesz pozwolić, aby testy prowadziły Twój projekt od dołu do góry lub od góry do dołu. Oba działają dobrze dla różnych programistów w różnych sytuacjach.Każde z tych podejść wymusi podjęcie pewnych "wstępnych" decyzji projektowych, ale to dobrze. Podejmowanie tych decyzji w celu napisania testów opiera się na testach!

W twoim przypadku masz pojęcie, jak powinien wyglądać zewnętrzny zewnętrzny interfejs do systemu, więc zacznijmy od tego. Napisz test na to, jak uważasz, że użytkownicy twojego klienta powiadomień powinni wchodzić z nim w interakcje i niech to się nie powiedzie. Ten test jest podstawą do testów akceptacyjnych lub integracyjnych i będą one nadal występować, dopóki opisywane funkcje nie zostaną zakończone. W porządku. Teraz zejdź na niższy poziom. Jakie kroki należy podjąć, aby zapewnić interfejs wysokiego poziomu? Czy możemy napisać test integracji lub jednostki dla tych kroków? Czy mają zależności, których nie uwzględniłeś, które mogą spowodować zmianę interfejsu centrum powiadomień, który zacząłeś definiować? Kontynuuj wykonywanie odwiertów po raz pierwszy definiując zachowanie podczas testów zakończonych niepowodzeniem, aż do momentu, w którym okaże się, że faktycznie osiągnąłeś test jednostkowy. Teraz zaimplementuj wystarczającą ilość, aby przejść test jednostki i kontynuować. Testy jednostek przechodzą, dopóki nie zbudujesz wystarczająco dużo, by przejść test integracyjny i tak dalej. W końcu ukończysz pierwszy etap budowy drzewa testów i powinieneś mieć dobrze przetestowaną funkcję, której konstrukcja została poparta twoimi testami.

2

zacząłbym z tej linii:

NotificationClient client = new NotificationClient("abcd1234"); // client ID 

Brzmi jak potrzebujemy NotificationClient, który potrzebuje identyfikator klienta. Łatwo to przetestować. Moja pierwsza próba może wyglądać:

public void testNewClientAbcd1234HasClientId() { 
    NotificationClient client = new NotificationClient("abcd1234"); 
    assertEquals("abcd1234", client.clientId()); 
} 

oczywiście, nie będzie kompilować na początku - nie aż ja napisałem klasę NotificationClient z konstruktora, który przyjmuje parametr ciąg i ClientID metoda(), która zwraca ciąg znaków - ale jest to część cyklu TDD.

public class NotificationClient { 
    public NotificationClient(string clientId) { 
    } 
    public string clientId() { 
     return ""; 
    } 
} 

W tym momencie mogę uruchomić mój test i oglądać to nie (bo mam zakodowane ClientID() 's powrotu być pusty ciąg). Raz mam moją uszkodzoną testów jednostkowych, piszę tylko tyle kodu produkcyjnego (w NotificationClient), aby uzyskać test pass:

public string clientId() { 
     return "abcd1234"; 
    } 

Teraz wszystkie moje testy przechodzą, więc mogę zastanowić się, co robić dalej. Oczywistą (dobrze, oczywiste mi) Następnym krokiem jest, aby upewnić się, że mogę tworzyć klientów, których identyfikator nie jest „abcd1234”:

public void testNewClientBcde2345HasClientId() { 
    NotificationClient client = new NotificationClient("bcde2345"); 
    assertEquals("bcde2345", client.clientId()); 
} 

uruchomić mój zestaw testowy i zauważają, że testNewClientBcde2345HasClientId() nie powiedzie się podczas testNewClientAbcd1234HasClientId() przechodzi, a teraz mam dobry powód, aby dodać zmienną składową do NotificationClient:

public class NotificationClient { 
    private string _clientId; 
    public NotificationClient(string clientId) { 
     _clientId = clientId; 
    } 
    public string clientId() { 
     return _clientId; 
    } 
} 

Zakładając brak błędów typograficznych nie podkradał się, że dostaniesz wszystkie moje testy przejść, a ja może przejść do następnego kroku. (W twoim przykładzie prawdopodobnie będzie to testowanie, że notifyOnEvent(Event.WRONG_EVENT) zwraca Response, którego codeValue() jest równe 1223.)

Czy to pomaga?

Powiązane problemy