adeptom #programowanie polecam myśleć o testach jednostkowych jako "zbiorze aksjomatów o kodzie których znajomości oczekujesz od próbujących rozwijać twój system".

dodatkowe twierdzenie do udowodnienia w swojej duszy dla chętnych - #sztucznainteligencja nie chce nic, więc większość testów jednostkowych które proponuje jest k⁎⁎wa bez sensu xD

#autyzm #psychiatria #komunikacja

Komentarze (8)

wombatDaiquiri

prompt: zaproponuj wpis na portal społecznościowy hejto na temat "co to testy jednostkowe"

Pewnie każdy coś słyszał, ale krótko i po ludzku

Co to są testy jednostkowe?

Testy jednostkowe to małe, automatyczne sprawdziany dla kodu.
Biorą najmniejszy sensowny kawałek programu (funkcję, metodę, klasę) i sprawdzają, czy:

  • dla danych wejściowych X

  • zwraca wynik Y

  • zawsze i przewidywalnie

To trochę jak test smaku w kuchni:

zanim podasz danie gościom, próbujesz łyżeczką sosu, a nie czekasz aż ktoś się zatruje.

Po co to komu?

  • szybciej rozumiesz własny kod (i cudzy)

  • zmiany nie psują rzeczy, które już działały

  • refaktoryzacja przestaje być rosyjską ruletką

  • mniej „działało u mnie”

Czego testy jednostkowe NIE robią:

  • nie sprawdzają UI

  • nie testują całego systemu

  • nie zastępują myślenia

Najczęstszy błąd:
„Napiszę testy na końcu”
(spoiler: na końcu już się ich nie pisze)

Dobrze napisany test jednostkowy to:

  • szybki

  • deterministyczny

  • czytelny

  • testuje jedną rzecz

A najlepszy moment na testy?
w chwili, gdy kod jeszcze boli od świeżości

Jeśli chcesz, w kolejnym wpisie mogę:

  • pokazać zły vs dobry test

  • albo wytłumaczyć testy bez frameworków i buzzwordów

malkontenthejterzyna

@wombatDaiquiri


Biorą najmniejszy sensowny kawałek programu

To jest mega nieoczywiste. Moim zdaniem AI często testuje zbyt nisko, betonując kod.


To jest mega artykuł na ten temat - w Pythonie, ale imo lang agnostic. Cała książka też sztos.

https://www.cosmicpython.com/book/chapter_05_high_gear_low_gear.html


Generalnie narzucanie architektury dla kodziku to jedna z ważniejszych rzeczy przy vibe-codowaniu.

dotevo

Ja ogólnie lubię TDD i wydaje mi się, że to najlepszy sposób na AI. Odpalasz takiego gemini cli i mowisz aby na podstawie wymagań (tutaj uzupelniasz) napisał Ci testy. Sprawdzasz czy mają sens i potem w kolejnej iteracji mówisz aby zaimplementował kod jtóry przejdzie testy i spełni wymagania. Potem każda poprawka to najpierw poprawa testów, a dopiero potem zmiany w kodzie. Takie coś u mnie się sprawdza

wombatDaiquiri

@dotevo


mowisz aby na podstawie wymagań (tutaj uzupelniasz)


Czyli dosłownie przekazujesz logikę testów a AI je tylko tłumaczy na język programowania? Czy w ogóle piszesz test i mówisz „uzupełnij przykłady dla następujących warunków brzegowych”? Pragnę zwrócić jedynie Twoją uwagę na fakt, ze całe „myslenie” tj. „podejmowanie decyzji na podstawie ekspertyzy” nadal w swoim podejściu odpowiadasz sam.

dotevo

@wombatDaiquiri ależ oczywiście. Zresztą osobiście mało używam AI w pracy. Za to do hobbistycznych projektów owszem bo mocno przyśpiesza pracę, a przy tym trenuję sobie jak pracować z AI.


Wydaje mi się, że dałoby radę napisać jednego dobrego prompta, który zrobi wszystko. Używam gemini-cli i piszę mu tylko "W pliku readme.md masz opis projektu, zaimplementuj go używając TDD w języku XYZ. Projekt będzie gotowy gdy ...., pamiętaj o podziale na moduły oraz kod musi być w języku angielskim". Potem mieli cały dzień i na koniec wychodzi coś sensownego. W prompcie najlepiej wprost powiedzieć na co musi zwrócić uwagę. Np. bezpieczeństwo, testy e2e, dokumentację itd.


Druga sprawa to czego używasz do kodowania? bo wg mnie chatgpt jest słaby, gemini ok, claude najlepszy - ale za free mało można.

baklazan

@wombatDaiquiri Mój problem z UT (po nastu latach pracy w zawodzie) jest taki, że są to duplikaty większości aksjomatów które w kodzie... już są. W kodzie.


Kod produkcyjny jest sam w sobie właśnie takim aksjomatem. Więc dodanie duplikatu powoduje w wielu (nie wszystkich!) wypadkach marnowanie czasu na pracę "żeby testy przeszły" po najmniejszej zmianie: czy to funkcjonalnej, czy refaktorze.


Dlatego z mojej perspektywy podwójnie chore są "progi pokrycia testami jednostkowymi" które spotykam w różnych firmach.


Gdyby tylko zamiast nad testami UT spędzić więcej czasu nad kodem produkcyjnym, przygotowaniem zmian... : ).


UT nie są zupełnie bezużyteczne. Nie wszystko da się łatwo wyczytać z kodu, wyłączam z tego zbioru UT na bardzo nietypowe edge case'y (typu reprodukcje bugów) i testy modułowe (które de facto nie są UT, ale to często te same narzędzia i ten sam run).

wombatDaiquiri

@baklazan ja osobiście potrzebuje „sanity check” ze każdy komponent który napisałem działa tak jak sie spodziewam zanim będę składał komponenty ze sobą. Ale ja pracuję też dość głęboko w infrastrukturze dużych serwisów, więc mogę mieć zboczenie na bezawaryjność.


Natomiast zazdroszczę ludziom którzy są przekonani ze napisali to co chcieli po tym jak to napiszą xD

baklazan

@wombatDaiquiri Nigdy nie jesteś pewien na 100%, więc testy są konieczne: po prostu z reguły UT są najmniej użyteczne. Generalnie chodzi o to żeby tworzyć zmiany małymi krokami, używając do tego bardzo małych commitów, jednocześnie nie zmieniając zachowania tam gdzie nie trzeba.


Podam Ci przykład jak do tego podchodzę:

Zmiana w aplikacji rozszerzającej zachowanie jakiegoś modułu.

Zaczynasz od refactorów, które mają nie zmieniać zachowania klasy. Każdy refactor to osobny commit. Każdy commit musi być czysty (tzn nie zawierać innych zmian, nie będących częścią tego co robisz), więc np:
- osobny commit na uporządkowanie zmiennych w funkcji, tak, żeby dało się jej część wyciągnąć robiąc niewiele więcej niż kopiuj-wklej,

- osobny commit na wydzielenie metody,

- itd.

Generalnie osobny commit na każdy krok refactoringowy. Podobnie przy dodawaniu nowego kodu, idealnie byłoby podzielić go na kawałki.


Każdy commit musi działać! (przechodzić wszystkie testy itp)


Z reguły zmiany wprowadzające zmianę zachowania są na końcu takiego brancha/patchsetu i powinny być tryawialne: żebyś był w stanie szybko je wycofać, ale też szybko wyłapać jakiś problem.


Wiadomo - przeoczysz wiele błędów. Ale jeśli przy każdym commicie przejrzysz zmiany które zrobiłeś, zmniejszasz szansę na wprowadzenie regresji, i to wielokrotnie. Pomaga przy tym pisanie dobrych opisów commitów: opisując nie "co zrobiłem" tylko "po co to zrobiłem" - to "co zrobiłeś" widać w commicie, zwłaszcza jeśli jest mały i prosty. Pytanie "dlaczego ten commit zrobiłeś" jest jeśli nie nieoczywiste to na pewno trudne do zgadnięcia. Finalnie: odpowiedź na to pytanie wiele razy sprawiła że wywaliłem dane zmiany jako kompletnie niepowiązane ; D.


To jest workflow do którego się trzeba przyzwyczaić, wymaga dużo czytania i rozumienia kodu, ale bardzo szybko powoduje, że nie dość że robię o wiele mniej błędów, to do tego bardzo dobrze rozumiem co się dzieje w kodzie - żeby przygotować dobry patchset musisz dobrze zrozumieć co robi kod w którym pracujesz.


Prywatnie zmieniło to moje podejście do pisania kodu o 180 stopni. Jeśli zaczniesz stosować ten workflow i nabierzesz wprawy zaczynasz pisać kod paradoksalnie bardzo szybko i bez błędów - nie marnuję czasu na debugowanie, bo najczęściej nie mam po co go robić.


I powoduje że UT są po prostu upierdliwe, bo przenoszę ciężar z "napisz kod produkcyjny i opisz go testami" na "napisz kod produkcyjny tak żeby działał i nic nie popsuł". Testuję go najczęściej tylko manualnie wysokopoziomowo, czasem piszę i odpalam testy modułowe/systemowe.

Zaloguj się aby komentować