Od jakiegoś czasu próbuję nauczyć się pisać testy jednostkowe dla mojego kodu.
Początkowo zacząłem robić prawdziwy TDD, w którym nie pisałbym żadnego kodu, dopóki nie napisałem najpierw testu zakończonego niepowodzeniem.
Jednak ostatnio miałem drażliwy problem do rozwiązania, który wymagał dużej ilości kodu. Po kilku tygodniach pisania testów, a potem pisania kodu, doszedłem do niefortunnego wniosku, że całe moje podejście się nie uda i będę musiał odrzucić dwa tygodnie pracy i zacząć od nowa.
Jest to wystarczająco zła decyzja, aby podjąć się, gdy właśnie napisałeś kod, ale kiedy napisałeś również kilkaset testów jednostkowych, jeszcze trudniej jest go wyrzucić.
Nie mogę przestać myśleć, że zmarnowałem 3 lub 4 dni na pisanie tych testów, kiedy mogłem po prostu skompletować kod dla potwierdzenia koncepcji, a następnie napisać testy, kiedy byłem zadowolony z mojego podejścia.
Jak osoby ćwiczące TDD właściwie radzą sobie z takimi sytuacjami? Czy w niektórych przypadkach można zginać reguły, czy zawsze najpierw niewolniczo piszesz testy, nawet jeśli kod ten może okazać się bezużyteczny?
źródło
Odpowiedzi:
Wydaje mi się, że są tutaj dwie kwestie. Po pierwsze, nie zdawałeś sobie sprawy, że oryginalny projekt może nie być najlepszym podejściem. Jeśli wiesz o tym z góry, być może zdecydowałeś się opracować prototyp szybkiego wyrzucania lub dwa, aby zbadać możliwe opcje projektowania i ocenić, który jest najbardziej obiecujący. W prototypowaniu nie musisz pisać kodu jakości produkcji i nie musisz testować jednostkowo każdego zakamarki (lub w ogóle), ponieważ jedynym celem jest nauka, a nie dopracowywanie kodu.
Teraz uświadomienie sobie, że potrzebujesz prototypowania i eksperymentów, a nie rozpoczęcie tworzenia kodu produkcyjnego od razu, nie zawsze jest łatwe i nie zawsze jest możliwe. Uzbrojeni w właśnie zdobytą wiedzę, możesz następnym razem rozpoznać potrzebę prototypowania. A może nie. Ale przynajmniej wiesz teraz, że należy rozważyć tę opcję. I to samo w sobie jest ważną wiedzą.
Drugi problem to IMHO z twoim postrzeganiem. Wszyscy popełniamy błędy i z perspektywy czasu łatwo jest zobaczyć, co powinniśmy zrobić inaczej. Tak właśnie się uczymy. Zapisz swoją inwestycję w testy jednostkowe, ponieważ cena nauki o prototypowaniu może być ważna, i przełam ją. Staraj się nie popełniać tego samego błędu dwa razy :-)
źródło
Chodzi o to, że TDD zmusza cię do pisania małych przyrostów kodu w małych funkcjach , właśnie w celu uniknięcia tego problemu. Jeśli spędziłeś tygodnie na pisaniu kodu w jednej domenie, a każda metoda, którą napisałeś, staje się bezużyteczna po ponownym przemyśleniu architektury, wówczas twoje metody są prawie na pewno zbyt duże. (Tak, wiem, że nie jest to teraz pocieszające ...)
źródło
Brooks powiedział „plan wyrzucenia jednego; w każdym razie będziesz”. Wydaje mi się, że właśnie to robisz. To powiedziawszy, powinieneś napisać testy jednostkowe w celu przetestowania jednostki kodu, a nie dużego pokosu kodu. Są to bardziej testy funkcjonalne i dlatego powinny być przeprowadzane w ramach każdej wewnętrznej implementacji.
Na przykład, jeśli chcę napisać solver PDE (częściowe równania różniczkowe), napisałbym kilka testów próbujących rozwiązać rzeczy, które mogę rozwiązać matematycznie. To moje pierwsze testy „jednostkowe” - czytaj: testy funkcjonalne uruchamiane w ramach frameworka xUnit. Nie zmienią się one w zależności od tego, jakiego algorytmu używam do rozwiązania PDE. Wszystko, na czym mi zależy, to wynik. Drugie testy jednostkowe skupią się na funkcjach używanych do kodowania algorytmu, a zatem będą specyficzne dla algorytmu - powiedzmy Runge-Kutta. Gdybym miał dowiedzieć się, że Runge-Kutta nie był odpowiedni, nadal miałbym te testy na najwyższym poziomie (w tym te, które wykazały, że Runge-Kutta nie był odpowiedni). Zatem druga iteracja nadal miałaby wiele takich samych testów jak pierwsza.
Twój problem może dotyczyć projektu, a niekoniecznie kodu. Ale bez dalszych szczegółów trudno powiedzieć.
źródło
Należy pamiętać, że TDD jest procesem iteracyjnym. Napisz mały test (w większości przypadków powinno wystarczyć kilka wierszy) i uruchom go. Test powinien zakończyć się niepowodzeniem, teraz należy bezpośrednio pracować na głównym źródle i spróbować wdrożyć testowaną funkcjonalność, aby test przeszedł pomyślnie. Teraz zacznij od nowa.
Nie powinieneś próbować pisać wszystkich testów za jednym razem, ponieważ, jak zauważyłeś, to się nie uda. Zmniejsza to ryzyko marnowania czasu na pisanie testów, które nie będą używane.
źródło
Myślę, że sam to powiedziałeś: nie byłeś pewien swojego podejścia, zanim zacząłeś pisać wszystkie testy jednostkowe.
Czego się nauczyłem, porównując rzeczywiste projekty TDD, z którymi pracowałem (w rzeczywistości nie tak wiele, tylko 3 obejmujące 2 lata pracy) z tym, czego nauczyłem się teoretycznie, to automatyczne testowanie! = Testowanie jednostkowe (oczywiście bez wzajemnego testowania Ekskluzywny).
Innymi słowy, T w TDD nie musi mieć ze sobą U ... Jest zautomatyzowany, ale jest mniej testem jednostkowym (jak w klasach i metodach testowych) niż automatyczny test funkcjonalny: jest na tym samym poziomie funkcjonalnej szczegółowości jako architektury, nad którą obecnie pracujesz. Zaczynasz na wysokim poziomie, z kilkoma testami i tylko funkcjonalnym dużym obrazem, i dopiero w końcu dostajesz tysiące UT, a wszystkie twoje klasy są dobrze zdefiniowane w pięknej architekturze ...
Testy jednostkowe zapewniają dużą pomoc podczas pracy w zespole, aby uniknąć zmian w kodzie, powodując niekończące się cykle błędów. Ale nigdy nie napisałem niczego tak precyzyjnego, kiedy zaczynałem pracę nad projektem, zanim nie miałem przynajmniej globalnego działającego POC dla każdej historii użytkownika.
Może to tylko mój osobisty sposób na zrobienie tego. Nie mam wystarczającego doświadczenia, aby od początku zdecydować, jakie wzory lub strukturę będzie miał mój projekt, więc rzeczywiście nie będę marnować czasu na pisanie setek UT od samego początku ...
Mówiąc bardziej ogólnie, pomysł zniszczenia wszystkiego i wyrzucenia wszystkiego zawsze będzie obecny. Choć „ciągłe”, jak możemy próbować być przy użyciu naszych narzędzi i metod, czasami jedynym sposobem na walkę z entropią jest rozpoczęcie od nowa. Ale celem jest to, że kiedy to nastąpi, zautomatyzowane i jednostkowe testy, które wdrożysz, sprawią, że Twój projekt będzie już mniej kosztowny, niż gdyby go nie było - i tak będzie, jeśli znajdziesz równowagę.
źródło
Połączenie testów jednostkowych z rozwojem opartym na testach jest źródłem wielu udręk i biada. Przejrzyjmy to jeszcze raz:
Podsumowując: testowanie jednostkowe koncentruje się na implementacji, TDD koncentruje się na wymaganiach. To nie to samo.
źródło
Rozwój oparty na testach ma na celu napędzanie rozwoju. Testy, które piszesz, pomagają potwierdzić poprawność pisanego kodu i zwiększają szybkość programowania od pierwszego wiersza.
Wydaje się, że wierzysz, że testy są obciążeniem i są przeznaczone tylko do późniejszego rozwoju. Ten sposób myślenia nie jest zgodny z TDD.
Może możesz to porównać do pisania statycznego: chociaż można pisać kod bez informacji o typie statycznym, dodanie typu statycznego do kodu pomaga w zapewnieniu pewnych właściwości kodu, uwalniając umysł i pozwalając skupić się na ważnej strukturze, zwiększając w ten sposób prędkość i skuteczność.
źródło
Problem z robieniem poważnego refaktoryzacji polega na tym, że możesz i czasami podążasz ścieżką, która prowadzi cię do zrozumienia, że odgryzłeś więcej niż możesz przeżuć. Wielkie refaktoryzacje są błędem. Jeśli projekt systemu jest wadliwy, to refaktoryzacja może zająć Ci tylko tyle czasu, zanim będziesz musiał podjąć trudną decyzję. Albo zostaw system bez zmian i obejdź go, albo zaplanuj przeprojektowanie i dokonaj poważnych zmian.
Jest jednak inny sposób. Prawdziwą zaletą refaktoryzacji kodu jest uproszczenie, łatwiejszy do odczytania, a nawet łatwiejszy w utrzymaniu. Gdy podejdziesz do problemu, którego nie masz pewności, zmieniasz zmianę, idziesz tak daleko, aby dowiedzieć się, gdzie może to prowadzić, aby dowiedzieć się więcej o problemie, a następnie wyrzucasz skok i zastosujesz nowe refaktoryzowanie w zależności od tego, co to skok nauczył cię. Chodzi o to, że naprawdę możesz poprawić swój kod z pewnością tylko wtedy, gdy kroki są małe, a wysiłki związane z refaktoryzacją nie przerastają twojej zdolności do pisania testów. Pokusa polega na napisaniu testu, następnie napisaniu kodu, a następnie napisaniu kodu, ponieważ rozwiązanie może wydawać się oczywiste, ale wkrótce zdajesz sobie sprawę, że twoja zmiana zmieni wiele innych testów, więc musisz uważać, aby zmieniać tylko jedną rzecz na raz.
Dlatego odpowiedzią jest, aby nigdy nie uczynić swojego refaktoryzacji ważnym. Dziecięce kroki. Zacznij od wyodrębnienia metod, a następnie spójrz na usunięcie duplikacji. Następnie przejdź do wyodrębniania klas. Każdy krok po kroku, drobna zmiana. JEŚLI wyodrębniasz kod, najpierw napisz test. Jeśli usuwasz kod, usuń go, uruchom testy i zdecyduj, czy którykolwiek z uszkodzonych testów będzie potrzebny. Jeden mały krok po kroku. Wydaje się, że zajmie to więcej czasu, ale w rzeczywistości znacznie skróci czas refaktoryzacji.
Rzeczywistość jest jednak taka, że każdy skok wydaje się potencjalnie stratą wysiłku. Czasami zmiany kodu nigdzie nie idą, a ty przywracasz kod z vcs. To tylko rzeczywistość tego, co robimy z dnia na dzień. Każdy kolec, który zawodzi, nie jest jednak marnowany, jeśli czegoś cię nauczy. Każdy wysiłek refaktoryzacji, który się nie powiedzie, nauczy cię, że albo próbujesz zbyt szybko zrobić zbyt wiele, albo że twoje podejście może być błędne. To też nie jest strata czasu, jeśli się czegoś nauczysz. Im częściej to robisz, tym więcej się uczysz i tym bardziej będziesz efektywny. Moja rada to po prostu na razie go nosić, nauczyć się robić więcej, robiąc mniej, i zaakceptować, że tak właśnie powinno być, dopóki nie będziesz w stanie lepiej określić, jak daleko zaostrzyć skok, zanim nigdzie cię to nie zaprowadzi.
źródło
Nie jestem pewien, dlaczego Twoje podejście okazało się wadliwe po 3 dniach. W zależności od niepewności w architekturze możesz rozważyć zmianę strategii testowania:
Jeśli nie masz pewności co do wydajności, możesz zacząć od kilku testów integracyjnych, które potwierdzają wydajność?
Gdy badasz złożoność interfejsu API, napisz kilka prawdziwych, małych testów jednostkowych, aby dowiedzieć się, jaki byłby najlepszy sposób, aby to zrobić. Nie zawracaj sobie głowy wdrażaniem czegokolwiek, po prostu spraw, aby Twoje klasy zwracały wartości zakodowane na stałe lub zmusza je do zgłaszania NotImplementedExceptions.
źródło
Dla mnie testy jednostkowe są również okazją do wprowadzenia interfejsu w „rzeczywiste” zastosowanie (cóż, tak prawdziwe, jak to tylko możliwe!).
Jeśli jestem zmuszony do przeprowadzenia testu, muszę wykonać swój projekt. Pomaga to zachować rozsądek (jeśli coś jest tak skomplikowane, że napisanie testu jest ciężarem, jak to będzie z niego korzystać?).
Nie pozwala to uniknąć zmian w projekcie, a raczej uwidacznia ich potrzebę. Tak, całkowite przepisanie jest uciążliwe. Aby (próbować) tego uniknąć, zwykle konfiguruję (jeden lub więcej) prototyp, prawdopodobnie w Pythonie (z ostatecznym opracowaniem w c ++).
To prawda, że nie zawsze masz czas na te wszystkie gadżety. To są dokładnie przypadki, w których potrzebujesz DUŻO czasu, aby osiągnąć swoje cele ... i / lub utrzymać wszystko pod kontrolą.
źródło
Witamy w cyrku kreatywnych programistów .
Zamiast szanować wszystkie „legalne / rozsądne” sposoby kodowania na początku,
spróbuj intuicji , przede wszystkim, jeśli jest to ważne i nowe dla Ciebie, a jeśli żadna próbka nie wygląda tak, jak chcesz:
- Pisz instynktownie z rzeczy, które już znasz , nie twoją mentalnością i wyobraźnią.
- I stój.
- Weź szkło powiększające i sprawdź wszystkie słowa, które piszesz: piszesz „tekst”, ponieważ „tekst” jest zbliżony do ciągu, ale potrzebny jest „czasownik”, „przymiotnik” lub coś dokładniejszego, przeczytaj ponownie i dostosuj metodę z nowym wyczuciem
. .. czy napisałeś fragment kodu, który myśli o przyszłości? usuń
- Popraw, zrób inne zadanie (sport, kultura lub inne rzeczy poza biznesem), wróć i przeczytaj ponownie.
- Wszystko dobrze pasuje,
- Poprawnie, zrób inne zadanie, wróć i przeczytaj ponownie.
- Wszystko pasuje dobrze, przekaż do TDD
- Teraz wszystko jest w porządku, dobrze
- Wypróbuj benchmark, aby wskazać rzeczy do optymalizacji, zrób to.
Co się pojawia:
- napisałeś kod zgodny z wszystkimi regułami
- dostajesz doświadczenie, nowy sposób pracy,
- coś zmienia się w twoim umyśle, nigdy nie będziesz się bał nowej konfiguracji.
A teraz, jeśli zobaczysz UML wyglądający jak wyżej, będziesz mógł powiedzieć
„Szefie, zaczynam od TDD w tym celu…”
to kolejna nowa rzecz?
„Szefie, spróbowałbym czegoś, zanim zdecyduję, w jaki sposób będę kodować ..”
Pozdrawiam PARIS
Claude
źródło