Film o Test Driven Development

Film o Test Driven Development
Marcin Lenkowski
  • Rejestracja:ponad rok
  • Ostatnio:8 miesięcy
  • Postów:20
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!

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 11 godzin
  • Lokalizacja:Laska, z Polski
  • Postów:10056
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.
edytowany 22x, ostatnio: Riddle
LukeJL
nie czytałem jeszcze całego posta, ale: 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. - zgadzam się. Bo wszystko zależy od asercji. Niestety robiąc coś za pierwszym razem łatwo można popaść w optymizm i sprawdzać tylko happy path, w porywach jeden unhappy path. Problem w tym, że tych "unhappy path" może być o wiele więcej, tylko robiąc to po prostu nie widzimy wszystkich przypadków krańcowych, nie wiemy, jak można zepsuć nasz kod, więc nie wiemy, jakie asercje dać.
LukeJL
np. powiedzmy piszesz parser, sprawdzasz czy parsuje poprawnie kod źródłowy, i sprawdzasz na wszelki wypadek, czy wykrywa np. jakiś oczywisty błąd składniowy. I już może się wydawać, że jest zrobione. A to nieprawda, bo sposobów na popsucie parsera jest dużo i kombinacji niepoprawnego kodu też.
Marcin Lenkowski
  • Rejestracja:ponad rok
  • Ostatnio:8 miesięcy
  • Postów:20
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 🤗

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 11 godzin
  • Lokalizacja:Laska, z Polski
  • Postów:10056
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ł.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)