Krótkie wprowadzenie do tego pytania. Używam teraz TDD, a ostatnio BDD od ponad roku. Używam technik takich jak kpina, aby bardziej efektywnie pisać testy. Ostatnio rozpocząłem osobisty projekt, aby napisać dla siebie mały program do zarządzania pieniędzmi. Ponieważ nie miałem wcześniejszego kodu, był to idealny projekt na początek z TDD. Niestety, tak bardzo nie doświadczyłem radości TDD. Zepsuło to nawet moją zabawę do tego stopnia, że zrezygnowałem z projektu.
W czym był problem? Cóż, zastosowałem podejście podobne do TDD, aby testy / wymagania ewoluowały w zakresie projektowania programu. Problem polegał na tym, że ponad połowa czasu programowania była na pisanie / testowanie refaktorów. W końcu nie chciałem już implementować żadnych funkcji, ponieważ musiałbym dokonać refaktoryzacji i napisać do wielu testów.
W pracy mam dużo starszego kodu. Tutaj piszę coraz więcej testów integracyjnych i akceptacyjnych oraz coraz mniej testów jednostkowych. Nie wydaje się to złym podejściem, ponieważ błędy są najczęściej wykrywane przez testy akceptacji i integracji.
Mój pomysł polegał na tym, że w końcu mogłem napisać więcej testów integracyjnych i akceptacyjnych niż testów jednostkowych. Jak powiedziałem do wykrywania błędów, testy jednostkowe nie są lepsze niż testy integracji / akceptacji. Testy jednostkowe są również dobre dla projektu. Ponieważ pisałem wiele z nich, moje klasy są zawsze zaprojektowane tak, aby były dobrze sprawdzalne. Ponadto podejście pozwalające testom / wymaganiom kierować projektem prowadzi w większości przypadków do lepszego projektu. Ostatnią zaletą testów jednostkowych jest to, że są one szybsze. Napisałem wystarczająco dużo testów integracyjnych, aby wiedzieć, że mogą one być prawie tak szybkie, jak testy jednostkowe.
Po przejrzeniu internetu dowiedziałem się, że istnieją bardzo podobne pomysły do moich, o których tu i tam wspomniano . Co sądzisz o tym pomyśle?
Edytować
Odpowiadając na pytania jeden przykład, w którym projekt był dobry, ale potrzebowałem ogromnego refaktoryzacji dla następnego wymagania:
Na początku były pewne wymagania do wykonania niektórych poleceń. Napisałem rozszerzalny parser poleceń - który przeanalizował polecenia z jakiegoś wiersza poleceń i nazwał poprawne w modelu. Wyniki zostały przedstawione w klasie modelu widoku:
Tu nie było nic złego. Wszystkie klasy były od siebie niezależne i mogłem łatwo dodawać nowe polecenia, pokazywać nowe dane.
Kolejnym wymaganiem było, aby każde polecenie miało własną reprezentację widoku - pewnego rodzaju podgląd wyniku polecenia. Przeprojektowałem program, aby uzyskać lepszy projekt nowego wymagania:
Było to również dobre, ponieważ teraz każde polecenie ma swój własny model widoku, a zatem własny podgląd.
Chodzi o to, że parser komend został zmieniony, aby używał parsowania komend opartego na tokenach i został pozbawiony możliwości wykonywania komend. Każde polecenie ma swój własny model widoku, a model widoku danych zna tylko bieżący model widoku poleceń, który zna dane, które należy pokazać.
W tym momencie chciałem tylko wiedzieć, czy nowy projekt nie złamał żadnych istniejących wymagań. Nie musiałem zmieniać ŻADNEGO testu akceptacyjnego. Musiałem przefakturować lub usunąć prawie KAŻDE testy jednostkowe, co było ogromnym nakładem pracy.
To, co chciałem tutaj pokazać, to powszechna sytuacja, która często zdarzała się podczas tworzenia. Nie było problemu ze starymi lub nowymi projektami, po prostu zmieniły się one naturalnie wraz z wymaganiami - jak to zrozumiałem, to jedna z zalet TDD, że projekt ewoluuje.
Wniosek
Dziękuję za wszystkie odpowiedzi i dyskusje. Podsumowując tę dyskusję, pomyślałem o podejściu, które przetestuję przy następnym projekcie.
- Przede wszystkim piszę wszystkie testy przed wdrożeniem czegoś, co zawsze robiłem.
- Dla wymagań piszę najpierw testy akceptacyjne, które testują cały program. Następnie piszę testy integracji dla komponentów, w których muszę zaimplementować wymaganie. Jeśli istnieje komponent ściśle współpracujący z innym komponentem w celu wdrożenia tego wymogu, napisałbym również kilka testów integracyjnych, w których oba komponenty są testowane razem. Wreszcie, jeśli muszę napisać algorytm lub inną klasę o wysokiej permutacji - np. Serializator - napisałbym testy jednostkowe dla tych konkretnych klas. Wszystkie pozostałe klasy nie są testowane, ale testy jednostkowe.
- W przypadku błędów proces można uprościć. Zwykle błąd jest powodowany przez jeden lub dwa komponenty. W tym przypadku napisałbym jeden test integracji dla komponentów testujących błąd. Jeśli dotyczy to algorytmu, napisałbym tylko test jednostkowy. Jeśli nie jest łatwo wykryć komponent, w którym występuje błąd, napisałbym test akceptacyjny, aby zlokalizować błąd - powinien to być wyjątek.