Film o Test Driven Development

1

Czy jest jakiś sposób, żeby to co pushujesz na produkcję zawsze działało?
Nie ma - ale testy zdecydowanie pomagają przynajmniej w ograniczeniu ryzyka.

Test Driven Development to sposób na pisanie kodu, w którym najpierw powstają testy, a dopiero później aplikacja.
Brzmi jak bezsens? To sprawdź jak działa!

W moim nowym filmie zobaczysz:

  • Jak podchodzę do TDD (owszem, nie zawsze na początku uruchamiane są testy) 🤔
  • Jak negocjuję (not) z klientami to czy mogę napisać testy 😎
  • Jak Test Driven Development mogło zaoszczędzić w NASA prawie 200.000.000$ 😱

Zapraszam do najnowszego odcinka na moim kanale!

2

Jest pare rzeczy na plus w tym filmie:

  • W sumie od 3:00 - 6:00 jest nawet bardzo dobrze. Testy to jest proces wytwarzania oprogramowania, nie należy pytać biznesów o pozwolenie. Także tutaj też 10/10.
  • Część "tym się różni junior od seniora", braniem odpowiedzialności za dostarczenie softu - też bardzo dobrze IMO.
  • W kontekście tego jak film jest zmontowany/edytowany, też nieźle.

Ale niestety moim zdaniem merytorycznie odn. TDD mogłoby być lepiej. W dużej mierze padło tutaj dużo rzeczy odnośnie tego jak Ty lubisz pracować, a mniej faktycznie o TDD. Zwróciłeś też dużą uwagę na szczegóły i kruczki, a o fundamentach TDD było raczej niewiele.

Rzeczy które są dobre, ale ich wyjaśnienie było złe:

  • Fajnie że zaczynasz od pustego testu; ale powiedziałeś że "napiszemy asercję czy true = false". Praktyka jest dobra, ale wyjaśnienie moim zdaniem jest nienajlepsze, i to z conajmniej kilku powodów:

    • Po pierwsze, zwraca niepotrzebnie uwagę na implementację, a nie na cel - brakuje po co to robimy?. Ktoś postronny, może stwierdzić "po co testować czy true nie jest false?" i będzie miał racje. Powiedziałeś że testujemy czy true jest false, i ktoś słusznie wytknie że to nie ma sensu - bo nie ma. Ale ja i Ty wiemy, że tak na prawdę nie testujemy czy true jest false; tylko tak na prawdę chcemy odpalić runner, i chcemy zobaczyć failującą asercję. Więc powinieneś to powiedzieć. (a to czy napiszemy failujący test robiąc assert(false), czy assert(1>2) czy w jakikolwiek inny sposób, to nie ma znaczenia).
    • Po drugie, to sprawia że osoby które praktykują TDD są postrzegani jako pedantyczni i robią coś "tylko dlatego że bo tak", aka "sztuka dla sztuki". Praktyki które stosujesz mają sens; ale nie kiedy daje się takie słabe wyjaśnienia jak Ty podałeś.

    Rozwiązanie:

    • ❗ Nie powinno być "testujemy czy true jest false" (bo to nie ma sensu, wiadomo że nie)
    • ✔️ Powino być "chcemy szybko uruchomić runner i zobaczyć failującą asercję" (to ma sens, jesteśmy pragmatyczni odn. procesu).

  • Fajnie że mówisz o integracji runnera z IDE, ale tutaj też wyjaśnienie jest słabe. Każdy powinien skonfigurować swoje środowisko tak jak chce. W Twoim filmiku bardzo silnie narzucasz komuś konfigurację która Tobie pasuje, ale nie koniecznie będzie dobra dla innych. I teraz ja rozumiem po co to robisz (a przynajmniej wydaje mi się), i to jest po to żeby zachęcić do szybkiego i częstego odpalania testów. Więc powinieneś to powiedzieć:

    Rozwiązanie:

    • ❗ Nie powinno być "ustaw phpunit.xml i integrację z IDE" (nic w TDD o tym nie mówi, i czemu masz narzucać komuś swój styl).
    • ❗ Nie powinno nawet być "skonfiguruj swoje środowisko tak żeby szybko i często uruchamiać testy" (to jest zwrócenie uwagi na formę, a nie na treść).
    • ✔️ Powino być "skonfiguruj swoje środowisko tak żeby mieć błyskawiczny feedback o swoim designie" (to jest to co daje faktyczną przewagę).

  • Fajnie że mówisz o Red/Green/Refactor, ale wyjaśnienie tego też moim zdaniem jest słabe. Po pierwsze, mogłeś powiedzieć że RGR nie jest kluczowe w TDD, tylko jest praktyką dodaną do TDD już później, jako wynikowa początkowych założeń TDD. Owszem - to jest spoko że RGR ktoś stosuje, ale to nie jest wartość sama w sobie. Wartością główną w TDD jest kierowanie wytwarzania software'u testami. To jest cała esencja TDD, której w Twoim filmiku zabrakło. Jeśli wyjdziemy od tego założenia, to prawdopodobne że będziemy kierować nasz software testami właśnie przez RGR ale to nie jest jedyna droga do tego celu.

    Rozwiązanie:

    • ❗ Nie powinno być "stosują Red-Green-Refactor"
    • ✔️ Powino być "kieruj swój design testami, jednym z lepszych sposobów na to jest red-green-refactor"

  • Fajnie że mówisz o "najpierw testy, potem kod", zwracając uwagę na to, że testy powinny być wcześniej; ale nie powiedziałeś czemu to jest istotne. Nauczysz kogoś stosowania tych "ceremonii", ale jeśli oglądający nie będzie wiedział czemu one służą, to nie pomoże mu to z TDD. Najpewniej wyjdzie osoba która myśli że stosuje TDD, podczas gdy tak na prawdę przyklepuje implementacje testami. Ja bym powiedział np. że pisząc testy stawiamy się w pozycji użytkownika naszego interfejsu, oraz możemy sprecyzować konkretne zachowanie którego oczekujemy, co jest trudniejsze jak już "siedzimy głęboko w kodzie". To byłoby dobre wyjaśnienie, którego zabrakło IMO.

  • Często pokazujesz kontrast testu i kodu, i to jest wartościowe - ale mówisz o tym w taki sposób, jakby testy miały tyczyć się kodu. Tymczasem, moim zdaniem to nie jest dobre, bo niepotrzebnie zwraca uwagę na implementację. IMO wszędzie tam gdzie mówisz "testy vs kod" powinieneś powiedzieć "testy vs zachowanie", bo to jest faktycznie istotne.

Rzeczy które faktycznie są on-point z TDD

  • Powiedziałeś że testy trzeba wytworzyć przed napisaniem implementacji. I to jest chyba jedyna rzecz do której nie można się przyczepić.

Rzeczy których moim zdaniem absolutnie powinno nie być

  • Zwróciłeś uwagę na "ceremonie", a nie na spodziewany efekt. Brakło mi w tym filmiku zwrócenia uwagi nie na "co robić", tylko "po co to robimy?".

  • Niepotrzebnie zwracasz uwagę oglądającego na implementację, co z resztą widać w tym jak piszesz w 9:40-10:30. Testy nie mają testować kodu - mają testować zachowanie, jakiś konkretny output. Dodatkowo mają być określeniem: "dla takiego wejścia, ma być takie wyjście". Niestety Twój filmik tego nie pokazuje; to co ja widzę w filmiku, to dla mnie to wygląda tak jakbyś napisał test który po prostu ma przyklepać implementację. Napisałeś testy który testuje że "kod który napisałeś jest kodem który napisałeś". Nie widzę tutaj kierowania designu testami 😕

  • Nikt kto stosuje TDD nie powie że "wrzuca kod na produkcję będąc pewnym że nie ma bugów". To jest po prostu nie prawda, TDD nie daje pewności braku bugów. Ktoś postronny oglądając ten filmik słusznie mógłby zauważyć że to nie możliwe; przez co to znowu buduje taką otoczkę że praktykanci TDD głoszą jakąś "wyidealizowaną" praktykę. Tego elementu powinno po prostu nie być.

    Jednak, jest prawdą że z TDD mamy lepszy design aplikacji. Jest prawdą, że bugów jest mniej, przez to że trudniej jest popełnić błąd. Jest prawdą, ze TDD pomaga budować lepsze aplikacje które mają mniej błędów. Jest też prawdą, że ponieważ mamy lepszy design, to pośrednio to wpływa na ilość błędów - bo ciężej jest nie zauważyć buga w lepszym designie. Takie rzeczy powinny się pojawić w Twoim filmie (i dziwię się że ich nie ma).

    Ale czy daje to pewność że bugów na produkcji nie ma? Na pewno nie.

  • Pierwszy test który napisałeś (pomijając pusty) nie testuje żadnego zachowania. Moim zdaniem to daje mylne wrażenie czym TDD jest. Myślę że gdybym ja miał pisać ten sam program, to:

    • ❗ nie zacząłbym od testu pod te 3 parametry w konstruktorze (bo po co mi on, skoro nie ma logiki)
    • ❗ nie zacząłbym od testu pod gettery (bo to nie jest żadne zachowanie, tylko interfejs klasy)
    • ✔️ tylko zastanowiłbym się jaki faktycznie problem próbuję rozwiązać - co mam na wejściu, i co chcę żeby było na wyjściu; co tak na prawdę próbuję zrobić. Jeśli to ma być lista zadań, to jednym z prostszych zachowań jest prawdopodobnie zaznaczenie czy zadanie na liście jest zrobione. To co Ty zrobiłeś, to zacząłeś od klas, konstruktorów, argumentów, asercje na typ klasy, count i gettery; czyli w zasadzie żadna konkretna logika, a jedynie przyklepanie implementacji. Szczerze mówiąc ja się zastanawiam czy to się powinno kwalifikować jako TDD, nawet jak masz najpierw testy - co z tego że testy sa najpierw, jak i tak Twój design nie jest kierowany testami?

  • Test który używa assertInstanceOf() moim zdaniem zupełnie nie jest w duchu TDD. Co takiego daje nam ta asercja, czego nie daje failujący test przez brak klasy? Nie opisuje to żadnej wartości, i równie dobrze można by ją wywalić. O testowaniu getterów nie wspomnę. Pierwszy faktyczny test który testuje jakieś zachowanie, to jest ten z count(), ale w momencie w którym go napisałeś masz już dużo kodu który nie był drive'owany testami.

Sposób na sprawdzenie czy stosujesz TDD, jest prosty: Jeśli Twoje decyzje projektowe są kierowane testami - to stosujesz TDD. Jeśli nie, to nie (Nie ważne czy masz rgr, czy piszesz testy najpierw, etc. to są tylko sposoby w jaki sposób chcemy drive'ować nasz design).

  • Trochę mnie martwi że tak wcześnie dodałeś mocki do testów. To przecież nie ma nic wspólnego z TDD. Prezentujesz to tak, jakby dodawanie mocków do testów to była taka domyślna rzecz, że "jeśli chcesz stosować TDD to używaj mocków", a tak nie jest przecież. Jeśli nowicjusz zobaczyłby takie korzystanie z mocków w teście, to bardzo szybko powstałoby 50-100 testów które nie mają w sobie nic oprócz mocków, testują za to typy klas i gettery, a faktyczna logika aplikacji po pierwsze nie jest przetestowana, a po drugie nie była kierowana testami. (Może dodam prywatnie, że według mnie ten mock tam w ogóle nie był potrzebny).

  • Martwi mnie też że testy które napisałeś są takie rozdmuchane, test który piszesz w 16:37 moim zdaniem jest dużo za duży; i nie sądzę że ktoś kto na prawdę stosuje TDD by taki napisał. Wiem, że gdybym ja pisał program o którym mówisz, to na pewno takiego testu bym nie stworzył. Jest za duży, ma dużo szczegółów, nie mówi o faktym zachowaniu które specyfikuje.

  • Stosujesz given/when/then, ale nie powiedziałeś że to z BDD.

  • Poza tym, to given/when/then jest trochę dziwne u Ciebie. Czemu wsadzasz parametry wejściowe do given? 😐 Jeśli dobrze pamiętam BDD, to w given powinien być początkowy stan systemu, a Twoja klasa TaskList to jest "pure-klasa", które nie potrzebuje początkowego stanu, więc jeśli to co piszesz to ma być BDD, to wszystkie "given" w Twoich testach powinny być puste. Chyba że to nie ma być BDD, tylko Twoje "given/when/then" to jest takie "input args/method/assert". Tylko że to znowu powoduje miskoncepcje, bo niby piszesz komentarze z BDD, ale używasz ich zupełnie inaczej. Na pewno początkującemu to tylko zamiesza w głowie niepotrzebnie.

  • Złe symptomy nie kierowania designu testami: 12:51 - piszesz test, na to że lista jest pusta (i to jest spoko), napisałeś nawet assertEmpty() (i to jest spoko), testy by teraz sfailował (spoko), więc w sumie powinieneś go odpalić i zobaczyć czy jest red (jeśli stosujesz rgr), więc teraz powinieneś zaimplementować tą logikę. Wchodzisz do kodu (spoko), ALE zamiast tego, dodajesz zwracany typ do take(), że ma zwrócić Task, mimo że nie napisałeś testu pod to. Dla mnie to jest 100% sygnał że nie kierujesz swojego software'u testami (bo gdybyś kierował, to napisałbyś test pod to co take() ma zwrócić). Więc moim zdaniem, to co widzę w filmiku to nie jest TDD 😕 I nie chodzi oczywiście o to że napisałeś jeden albo drugi test źle. Chodzi o to że w Twoim filmiku decyzje projektowe nie są kierowane testami. A cały sens TDD polega na tym żeby wszystkie decyzje projektowe były kierowane testami.

Rzeczy które powinny być, a ich nie ma

  • Brakło mi na filmiku desginowania software'u testami - czyli w zasadzie brakło TDD. To co ja widzę jak piszesz TaskList, to jakbyś już na starcie podjął decyzje projektowe, a potem napisał testy pod nie (znów to wygląda jak "przyklepanie"). TDD jest również techniką do projektowania software'u, a w Twoim filmiku tego w ogóle nie widać. Efekt jest taki sam jakbyś najpierw napisał kod, a potem testy.

  • Filmik wygląda jakby był nakręcony nie przez kogoś kto faktycznie praktykuje TDD, ale przez kogoś kto oglądał z boku kogoś kto pratykuje TDD. Tzn. opisujesz "to co widać" (rgr, phpunit, runner, testy, asercje), ale nie widać tego "sedna", tej treści. Nie widać po nim po co to robimy, co to daje, przed czym się ustrzegać, jakie są konekwencje pracy w taki sposób, ale też jakie są zalety. Dla mnie ten filmik wygląda tak jakby początkujący muzyk stworzył filmik "Jak grać na wiolączeli?", i w filmiku nie było słowa o nutach, melodii, etc, ale za to było "trzymaj palce w taki sposób, kup takie krzesło, machają ręką tak".

  • Istotnym czynnikiem w TDD jest feedback loop - jak szybko dostajemy informacje zwrotne o naszym designie. Feedback loop jest kluczowy w TDD, i najlepiej jakby był jaknajkrótszy. Nie da się praktykować TDD bez krótkiego feedback loop'a, bo wtedy siłą rzeczy software który wyznaczamy nie może być kierowany przez testy - jak mógłby, skoro nie dostajemy feedbacku?

Podusmowanie

Moim zdaniem ten filmik ma dwie cechy:

  • Po pierwsze, jak ja go oglądam, to mi bardziej wygląda jak prezentacja tego jak Ty stosujesz TDD, a nie filmik instruktarzowy dla początkujących (dużo tu jest Twoich przekonań, twoich odczuć, i Twoich praktyk), a mało jest rzeczy które faktycznie są w TDD kluczowe.
  • Po drugie, cały filmik jest w takim przeświadczeniu że Red-Green-Refactor to jest całe TDD, i wystarczy tylko robic Red-Green-Refactor żęby robić TDD i tyle. A to chyba nie o to chodzi.
0

Hej!
Bardzo dużo wartościowych uwag, dzięki.

Widzę że masz sporą wiedzę!
YouTube rządzi się też swoimi prawami, a ja zawsze robię to tak jak ja chcę to pokazać.

Dzięki jednak za komentarze, bo ludziom na forum świetnie uzupełniają pełniejszy obraz 🤗

0

Trochę to wziąłem personalnie; bo bardzo ciężko jest oduczyć kogoś kto myśli że umie TDD złych nawyków które sobie narobił.

1 użytkowników online, w tym zalogowanych: 0, gości: 1