Jak przetestować metodę która zmienia prywatne pole?

Jak przetestować metodę która zmienia prywatne pole?

Wątek przeniesiony 2023-06-12 18:13 z C# i .NET przez Riddle.

KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
2

Test read powinien polegać na tym, że masz jakiś predefiniowany input.file i sprawdzasz, czy to co read wypluło jest zgodne z tym, co według ciebie wypluć powinno.
W przypadku write masz sytuację odwrotną - czyli wrzucasz jakąś strukturę danych do write, tworzy ci się jakiś output.file, i na końcu sprawdzasz, czy ten plik jest zgodny z tym, co być powinno.

No ale teraz wg wcześniejszej logiki @Riddle testujesz prywatną implementację read / write. Bo zapis do pliku to przecież szczegół implementacyjny ;)

I tak, dopuszczam możliwość, że istnieją jakieś bardzo wyjątkowe sytuacje, w których monitorowanie prywatnych pól będzie uzasadnione. Ale są to właśnie wyjątkowe sytuacje, a co do zasady powinno się tego unikać.

A widzisz, i być może tu mamy różnicę poglądów wynikającą z rodzaju oprogramowania jaki piszemy.
Ja piszę oprogramowanie, które jest na ścieżce krytycznej wielu systemów i ma dość wysoko podniesioną poprzeczkę niezawodnościową.
Monitorowanie (metryki) oraz testowanie wewnętrznego stanu systemu (zarówno offline w postaci testów jak i online w postaci asercji działających na produkcji), plus bardzo rygorystyczne założenia wewnętrzne dotyczące budowy tych szczegółów implementacyjnych to podstawa budowy niezawodnych systemów.

Popatrz sobie na to zdjęcie:

screenshot-20230615112111.png

(obraz za wikipedia, Airbus A340)

Dlaczego tu jest taki pierdyliard przełączników, manipulatorów, wskaźników i ekranów?
Przecież interfejs publiczny samolotu jest dużo prostszy:

  • wolant / joystick / pedały do obsługi powierzchni sterowych
  • przepustnica do sterowania ciągiem
  • wajcha do wysuwania / wsuwania klap
  • podstawowa orientacja w przestrzeni: prędkość, pułap, sztuczny horyzont / busola
  • hamulce, podwozie
  • parę drobiazgów aby to wszystko włączyć / wyłączyć

A właściwie to można to nawet bardziej zredukować i zrobić samolot z trybem tylko automatycznym.
Nakręcisz sobie prędkość / wysokość / azymut na AP i lecisz. A lądujesz na ILS kat 3. Dziecko by umiało. :D

Po grzyba Ci wskaźnik ciśnienia płynu hydraulicznego, prędkość wirnika silnika 2, wskazanie ilości paliwa w zbiornikach, czy aż pięć odczytów wysokości?
Po co wskaźnik wysunięcia podwozia? :D

Myk polega na tym, że jak cokolwiek pójdzie nie tak, to musisz mieć możliwość po pierwsze:

  • wiedzieć że coś poszło nie tak, zanim powiedzą Ci o tym testy end-to-end (rozbiłeś się, koniec)
  • wiedzieć również możliwie najdokładniej co nie działa aby móc zareagować adekwatnie i szybko
edytowany 2x, ostatnio: Krolik
Riddle
No ale teraz wg Twjej wcześniejszej logiki... - do kogo Ty to piszesz? Przecież @wartek01 się dopiero pierwszy raz wypowiada w tym temacie?
KR
mea culpa, faktycznie myślałem, że odpowiadam Tobie
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
Krolik napisał(a):

Dlaczego tu jest taki pierdyliard przełączników, manipulatorów, wskaźników i ekranów?
Przecież interfejs publiczny samolotu jest dużo prostszy:

Zależy dla kogo.

  • Dla ludzi spoza samolotu albo pasażerów, to tak - to nie jest publiczny interfejs, i żaden pasażer nie powinien nic wiedzieć o tym pierdyliardzie przełączników
  • Ale dla pilotów, to już jest publiczny interfejs, każdy jeden z tych maleńkich przełączników jest publicznym interfejsem tego samolotu.

Więc pytanie, z jakiej perspektywy zadajesz to pytanie.

KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
1
  • Ale dla pilotów, to już jest publiczny interfejs, każdy jeden z tych maleńkich przełączników jest publicznym interfejsem tego samolotu.

Więc pytanie, z jakiej perspektywy zadajesz to pytanie.

Jednak istotą 90% tego interfejsu jest niezawodność / bezpieczeństwo, a nie główny cel biznesowy tj. dolecenie do celu w dobrych warunkach.

Abstrahując od samego lotu (tj produkcji jakbyśmy to nazwali u nas) zauważ też, że raczej nikt tam nie kwestionuje, że offline powinno się testować najmniejszą śrubkę czy łopatkę, nawet jeśli ona jest tylko bardzo prywatnym komponentem innego komponentu.
I jak najbardziej w testach będziesz testował np. takie rzeczy, czy po przyłożeniu odpowiedniej siły nie zmienił się np. kolor / widzialna struktura materiału tejże łopatki. Mimo że od samego koloru nic "biznesowo" nie zależy, to może to być proxy dla uszkodzeń, więc służy jako metoda diagnostyczna. Czyli jest sens testować prywatną metodę, jeśli od jej działania zależeć może działanie większego kawałka i testować nawet zgodność z założeniami wewnętrznymi, które nie mają związku z zewnętrznymi założeniami biznesowymi.

edytowany 2x, ostatnio: Krolik
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
Krolik napisał(a):

Test read powinien polegać na tym, że masz jakiś predefiniowany input.file i sprawdzasz, czy to co read wypluło jest zgodne z tym, co według ciebie wypluć powinno.
W przypadku write masz sytuację odwrotną - czyli wrzucasz jakąś strukturę danych do write, tworzy ci się jakiś output.file, i na końcu sprawdzasz, czy ten plik jest zgodny z tym, co być powinno.

No ale teraz wg wcześniejszej logiki @Riddle testujesz prywatną implementację read / write. Bo zapis do pliku to przecież szczegół implementacyjny ;)

Znowu, zależy - co to jest za program.

  • Jeśli program operuje na plikach (np cat, tail, vim, nano, less) to taki odczyt jest interfejsem, i należy go odpowiednio przetestować, i wtedy możesz mieć osobne testy dla read i write
  • Ale jeśli program nie operuje na plikach, a jedynie używa ich wewnętrznie, jako cache, jak np ngrok jest tylko tunelem między dwoma HTTP, a plików używa jako cache, to wtedy już nie możesz takich plików traktować jak interfejs i nie powinieneś o nich wiedzieć.

Istotą jest, po co piszesz jakiś kod:

  • Piszesz jakiś kod, bo ten kod jestem celem samym w sobie - wtedy napisz test pod to.
  • Piszesz kod, jako środek do celu (np hashmapa, jako środek żeby coś znaleźć), wtedy nie pisz testu pod to, bo ten test Ci tylko zaszkodzi.
Krolik napisał(a):
  • Ale dla pilotów, to już jest publiczny interfejs, każdy jeden z tych maleńkich przełączników jest publicznym interfejsem tego samolotu.

Więc pytanie, z jakiej perspektywy zadajesz to pytanie.

Jednak istotą 90% tego interfejsu jest niezawodność / bezpieczeństwo, a nie główny cel biznesowy tj. dolecenie do celu w dobrych warunkach.

Powiedziałbym że oba cele są do osiągnięcia, zarówno niezawodność, bezpieczeństwo jak i cel biznesowy.

I jak najbardziej w testach będziesz testował np. takie rzeczy, czy po przyłożeniu odpowiedniej siły nie zmienił się np. kolor / widzialna struktura materiału tejże łopatki. Mimo że od samego koloru nic "biznesowo" nie zależy, to może to być proxy dla uszkodzeń, więc służy jako metoda diagnostyczna. Czyli jest sens testować prywatną metodę, jeśli od jej działania zależeć może działanie większego kawałka i testować nawet zgodność z założeniami wewnętrznymi, które nie mają związku z zewnętrznymi założeniami biznesowymi.

Tylko wiesz, odchodzimy teraz od tematu już. Bo jeśli mówimy o normalnej aplikacji, takiej o jaką za pewne pytał @bakunet ; to nie. Wtedy testujesz publiczny interfejs, a prywatne elementy zostawiasz w spokoju i nie dodajesz żadnych metody diagnostycznych.

Ale jeśli wejdziemy w temat programu który Ty wyciągnąłeś; to to jest trochę inne pytanie, i możnaby pewnie o nim pogadać w innym wątku. Rozumiem, że w projekcie w którym mówisz, musisz być pewny że aplikacja działa, i żeby się upewnić że ona działa to musisz znać stan wielu małych przełączników i prywatnych elementów. Jeśli jest tak w istocie, że musisz znać ich stan i nie przeszkadza Ci że znajomość tego stanu utrudni Ci refaktor; no to z definicji moim zdaniem to co nazywasz "prywatnymi elementami" w gruncie rzeczy wcale nie jest prywatne - jest publiczne. Musi być zdefiniowane jako publiczne, jeśli znajomość tych elementów jest Ci konieczna do weryfikacji czy aplikacja działa poprawnie czy nie. (i nie mówię tutaj o Javowych słowach private i public, tylko mówię o klasyfikacji oprogramowania language-agnostic).

edytowany 1x, ostatnio: Riddle
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
Krolik napisał(a):

Sudoku jest taką łamigłówką, gdzie właśnie łatwo robić bardzo długą listę specyficznych, prostych przypadków, które wiadomo jak powinny działać, ale można nadal kompletnie nie wiedzieć jak rozwiązać problem w sposób ogólny.

Tak.

I moja tezza jest taka, że TDD w żadnym stopniu nie przybliża Cię do rozwiązania ogólnego, a wręcz przeszkadza. Przeszkadza, bo Roy skoncentrował się na jakiś duperelach związanych z obsługą planszy, zamiast ugryźć problem, wymyślić algorytm i na końcu napisać testy.

Tylko widzisz, że całkowicie odbiegamy od tematu wcześniejszego?

No ale niech będzie, odpowiem. Czyli nie podoba Ci się że autor tego przykładu obkodził rzeczy dookoła tego programu, zamiast jakby "iść po złoto" i od razu napisać algorytm. I z tego powodu twierdzisz, że TDD przeszkadza.

No to mam na to dwie odpowiedzi:

  • Po pierwsze, jeśli Ty stosowałbyś TDD, nie koniecznie musiałbyś wziąć tą samą drogę którą wziął autor. Mógłbyś od razu jechać po złoto, i od razu napisać testy pod faktyczny algorytm. To że ten gość tak do tego podszedł, nie znaczy że Ty musiałbyś.
  • Po drugie, TDD daje różne zalety, zależnie od tego od jakiego punktu wychodzisz:
    • Jeśli wiesz co czego dążysz (czyli znasz algorytm na rozwiązywanie sudoku, albo wiesz że napisałbyś taki łatwo), to śmiało, napisz - be my guest, TDD Ci w tym nie przeszkadza
    • Ale może być tak że nie wiesz, jaki jest dokładny algorytm - i wtedy podejścia autora tego postu jest jak najbardziej poprawne, bo pozwala iteratywnie móc wyewoluować taki algorytm, a potem w miarę możliwości go zrefaktorować. Także to jest by the book.
KR
Dlatego o TDD pisałem w komentarzach. Bo to jest offtop
KR
Mógłbyś od razu jechać po złoto, i od razu napisać testy pod faktyczny algorytm. Napisałbym algorytm, potem testy.
KR
Także to jest by the book. Tak, on robił by the book. Problem w tym, że iteratywnie nie wyewoluował tego algorytmu przez 5 postów. Czyli fail TDD. Jak nie znasz algorytmu to są znacznie lepsze metody niż TDD aby do algorytmu dojść. Metody stosowane przez matematyków i inżynierów dekady przed TDD. W dużym skrócie to się nazwa "analityczne / abstrakcyjne myślenie".
Riddle
@Krolik: Spoko, "analityczne/abstrakcyjne myślenie" zawsze spoko. Ja powiedziałbym że podejściu TDD, bliżej jest do metody naukowej. Question/hypothesis/experiment, to jest właściwie fail/code/pass z TDD. Nie będę się kłócił - jeśli stosujesz "abstrakcyjne analityczne myślenie", i chcesz wymyślić wszystko samemu, bądź moim gościem, możesz to stosować i na pewno też osiągniesz spoko kod. Nie będę się kłócił że TDD to jest jakieś najlepsze nie wiadomo co, inne metody też są dobre. Ale jeśli zechesz potem napisać testy pod ten kod; to moim zdaniem lepiej wtedy zacząć od TDD.
Riddle
Ale na począku powiedziałeś że TDD utrudnia powstawanie wysokiej jakości kodu i powoduje drabinki ifów (potem zmieniłeś na małe funkcje); i z tym się nie zgadzam całkowicie. @Krolik Przeczytałeś może mój przykład projektu https://github.com/t-regx/crlf ?
KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
2

Ale jeśli program nie operuje na plikach, a jedynie używa ich wewnętrznie, jako cache, jak np ngrok jest tylko tunelem między dwoma HTTP, a plików używa jako cache, to wtedy już nie możesz takich plików traktować jak interfejs i nie powinieneś o nich wiedzieć.

No z tym się właśnie nie zgadzam. To że jest traktowany wewnętrznie nie oznacza, że dla developerów powinien być tylko czarną skrzynką. Nie, ten plik nadal ma zapewne jakąś strukturę, jest (powinien być) udokumentowany na wewnętrzne potrzeby projektu. A skoro tak, to dobrze mieć testy weryfikujące czy kod faktycznie produkuje odpowiednią strukturę i czy odpowiednio ją czyta.

To że Ty jako pasażer (użytkownik) nie masz wglądu w wewnętrzne bebechy samolotu nie oznacza, że takiego wglądu nie powinni mieć mechanicy / piloci. A testy piszemy z punktu widzenia developera (czyli odpowiednika producenta / serwisu samolotu) a nie użytkownika (pasażera).

edytowany 1x, ostatnio: Krolik
W0
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 2 godziny
  • Postów:3540
0
Krolik napisał(a):

Test read powinien polegać na tym, że masz jakiś predefiniowany input.file i sprawdzasz, czy to co read wypluło jest zgodne z tym, co według ciebie wypluć powinno.
W przypadku write masz sytuację odwrotną - czyli wrzucasz jakąś strukturę danych do write, tworzy ci się jakiś output.file, i na końcu sprawdzasz, czy ten plik jest zgodny z tym, co być powinno.

No ale teraz wg wcześniejszej logiki @Riddle testujesz prywatną implementację read / write. Bo zapis do pliku to przecież szczegół implementacyjny ;)

Zarówno forma, jak i rodzaj pliku może być funkcjonalnością. Czym innym funkcjonalnie jest serializacja do JSONa, a czym innym jest serializacja do XMLa.

A widzisz, i być może tu mamy różnicę poglądów wynikającą z rodzaju oprogramowania jaki piszemy.
Ja piszę oprogramowanie, które jest na ścieżce krytycznej wielu systemów i ma dość wysoko podniesioną poprzeczkę niezawodnościową.
Monitorowanie (metryki) oraz testowanie wewnętrznego stanu systemu (zarówno offline w postaci testów jak i online w postaci asercji działających na produkcji), plus bardzo rygorystyczne założenia wewnętrzne dotyczące budowy tych szczegółów implementacyjnych to podstawa budowy niezawodnych systemów.

Wydaje mi się, że bardzo mocno schodzisz z tematu. Wypowiedziałem się na temat testów jednostkowych, natomiast ty mi tutaj dajesz wykład o monitorowaniu działania oprogramowania. Testy jednostkowe, testy integracyjne oraz monitorowanie działania systemu to są zupełnie różne rzeczy.

W przypadku monitoringu wewnętrzny stan powinien być udostępniony poza scope klasy i wtedy możesz sobie spokojnie to przecież przetestować jednostkowo. I nie ma tutaj większego znaczenia, czy owo udostępnienie odbywa się przez stworzenie jakiegoś gettera, zamianę private na public, dorzucenie jakiegoś rejestru prometheusowego czy wspomniane przez ciebie asercje.

edytowany 1x, ostatnio: wartek01
Zobacz pozostałe 5 komentarzy
Riddle
@wartek01: Nikt tak nie twierdzi
W0
Na mój tekst o tym, dlaczego nie należy sprawdzać prywatnego stanu klasy podczas testów jednostkowych @Krolik odpisał, że nie mam racji bo w skomplikowanych systemach wewnętrzny stan się upublicznia i monitoruje. Więc tak, twierdzę, że ktoś tak twierdzi :)
KR
@wartek01: moniotorowanie w czasie działania nie jest oczywiście tym samym co testowanie offline, ale jednak zastanówmy się - czy jest jakiś powód aby w monitorowaniu stanu aplikacji sprawdzanie stanu prywatnego miałoby być ok, a już w testach niby nie? Te tematy są jednak związane. Np. my w wielu testach weryfikujemy wartości metryk. A te metryki są obliczane często na podstawie stanu prywatnego. Lepiej w końcu odkryć błąd w stanie prywatnym w testach offline niż na produkcji.
Riddle
@Krolik: No to w takim razie to co Ty nazywasz "stan prywatny", tak na prawdę nie jest prywatne.
W0
@Krolik: jeszcze raz - w takim wypadku stan wewnętrzny monitorowanego obiektu jest w pewien sposób udostępniany na zewnątrz - a więc i da się go przetestować jednostkowo i powinno się go testować jednostkowo.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
wartek01 napisał(a):
Krolik napisał(a):

Test read powinien polegać na tym, że masz jakiś predefiniowany input.file i sprawdzasz, czy to co read wypluło jest zgodne z tym, co według ciebie wypluć powinno.
W przypadku write masz sytuację odwrotną - czyli wrzucasz jakąś strukturę danych do write, tworzy ci się jakiś output.file, i na końcu sprawdzasz, czy ten plik jest zgodny z tym, co być powinno.

No ale teraz wg wcześniejszej logiki @Riddle testujesz prywatną implementację read / write. Bo zapis do pliku to przecież szczegół implementacyjny ;)

Zarówno forma, jak i rodzaj pliku może być funkcjonalnością. Czym innym funkcjonalnie jest serializacja do JSONa, a czym innym jest serializacja do XMLa.

Jeden i ten sam kod może być albo szczegółem implementacyjnym albo funkcjonalnością - wszystko zależy od kontekstu w jakim się znajduje.

Krolik napisał(a):

Ale jeśli program nie operuje na plikach, a jedynie używa ich wewnętrznie, jako cache, jak np ngrok jest tylko tunelem między dwoma HTTP, a plików używa jako cache, to wtedy już nie możesz takich plików traktować jak interfejs i nie powinieneś o nich wiedzieć.

No z tym się właśnie nie zgadzam. To że jest traktowany wewnętrznie nie oznacza, że dla developerów powinien być tylko czarną skrzynką. Nie, ten plik nadal ma zapewne jakąś strukturę, jest (powinien być) udokumentowany na wewnętrzne potrzeby projektu. A skoro tak, to dobrze mieć testy.

No, tak i nie.

  • Czy Ty jako deweloper musisz znać strukturę tego pliku, żeby móc z nim pracować? Owszem.
  • Czy Twój kod musi znać dokładną strukturę tego pliku - również tak.

I teraz pytanie za milion dolarów:

  • Czy jeśli zmienię strukturę tego pliku, ale w taki sposób ze aplikacja nadal działa tak jak działała i nie ma z tego powodu żadnego buga, to czy jakikolwiek test powinien się wywalić?

Moim zdaniem żaden test nie powinien się wywalić, @Krolik jakie jest Twoje zdanie?

edytowany 2x, ostatnio: Riddle
KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
1

Czy jeśli zmienię strukturę tego pliku, ale w taki sposób ze aplikacja nadal działa tak jak działała i nie ma z tego powodu żadnego buga, to czy jakikolwiek test powinien się wywalić?

Jeśli zmieniłeś strukturę tego pliku tak że nie jest już zgodna z dokumentacją (nawet jedynie wewnętrzną / design-doc) i narusza jakieś wewnętrzne niezmienniki przyjęte w założeniach dla tych plików (znowu - mogą to być nawet tylko zasady wewnętrzne, których nie widzi klient) to jak najbardziej powinien się wywalić. I to nawet jeśli testy end-to-end nadal działają ok.

edytowany 1x, ostatnio: Krolik
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
Krolik napisał(a):

Czy jeśli zmienię strukturę tego pliku, ale w taki sposób ze aplikacja nadal działa tak jak działała i nie ma z tego powodu żadnego buga, to czy jakikolwiek test powinien się wywalić?

Jeśli zmieniłeś strukturę tego pliku tak że nie jest już zgodna z dokumentacją (nawet jedynie wewnętrzną / design-doc) i narusza jakieś wewnętrzne niezmienniki przyjęte w założeniach dla tych plików (znowu - mogą to być nawet tylko zasady wewnętrzne, których nie widzi klient) to jak najbardziej powinien się wywalić. I to nawet jeśli testy end-to-end nadal działają ok.

Jaki zysk będziesz miał z tego że się wywalą w takiej sytuacji?

Owszem, wywalą się, i będziesz mógł wtedy poprawić ten plik. Ale pytanie się pojawia czemu miałbyś to zrobić, skoro aplikacja nadal działa tak jak działała? Możesz ją wdrożyć i nie będzie regresji, użytkownicy nadal będą mogli z niej korzystać tak jak korzystali, więc pojawia się pytanie, czemu testy miałyby Ci zabronić to zrobić? Nie ma programistycznego powodu żeby to zrobić - jedyne powody jakie są to jakieś formalne/organizacyjne (typu czy plik pasuje do jakiegoś tam szablonu), i to oczywiście też jest okej, tylko to nie powinno być enforce'owane przez testy. Czy dla Ciebie testy nie powinny być odpowiedzią na pytanie "czy aplikacja działa?"?

Bo tym są testy dla mnie, są odpowiedzią na pytanie:

  • czy można zdeployować aplikacje
  • czy aplikacja działa
  • czy aplikacja ma błędy

Pytanie o którym Ty mówisz, czyli "czy plik pasuje do szablonu organizacji" albo "czy dokumentacja pliku pasuje do tego co jest w tym pliku" to są ważne pytania, oczywiście, ale to powinno być ogarnięte w inny sposób. W taki, który nie przeszkadza w developmencie; a testy które wiedzą o swoich internalsach właśnie przeszkadzają.

edytowany 7x, ostatnio: Riddle
KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
2

Jaki zysk będziesz miał z tego że się wywalą w takiej sytuacji?

  • Mogą mnie uchronić przed potencjalną katastrofą w przyszłości. Świat nie jest idealny i testy nie są w stanie przetestować wszystkiego. Możliwe że ten wywalający się test w jakiejś implementacji tak naprawdę oznacza błąd również z punktu widzenia bizensu, ale testy wyższego poziomu go nie złapały. Przykładowo, drzewo miało być zrównoważone, okazało się że nie jest, ale poza tym działa i cała reszta jest happy. Testy end-to-end mogą tego nie wyłapać bo np. nie było dostatecznie dużo danych aby zauważyć wpływ na wydajność.

  • Ułatwiają rozwój systemu poprzez zwiększanie przewidywalności jak się zachowują poszczególne komponenty. Konkretny przykład - ostatnio natrafiłem na prywatną metodę, która realizowała w sumie dość prosty algorytm na kolekcji czegoś tam. Okazało się, że poprawnie działała tylko wtedy jeśli liczba elementów w kolekcji <= 2. Dla większej liczby był subtelny błąd. Błąd jednak nigdy nie został wyłapany, bo wszystkie dotychczasowe przypadki testowe (i biznesowe zarazem) szczęśliwie generowały tylko 0, 1 lub 2 elementy i nigdy nie było wstawiane więcej. Metoda nie miała testów, bo była prywatna. Ja dodawałem nowy ficzer w zupełnie innym miejscu, który spowodował, że nagle tam lądowały 3 elementy. I nagle kod zaczął się zachowywać dziwnie. Jakby ta metoda miała testy albo przynajmniej asercję na wejściu (co też jest formą testu), to zaoszczedziłoby mi to długiej sesji debugowania.

  • Dają jasny sygnał - zrobiłem coś niezgodnego ze specyfikacją (która stanowi pewien konsensus zespołowy - wszyscy się zgodzili że takie założenia będą i staramy się ich trzymać). Ktoś inny będzie dodawał po mnie nową funkcję i będzie polegał na tej uzgodnionej specyfikacji to będzie zaskoczony tym, że kod działa inaczej. Wniosek: albo poprawiam kod aby był zgodny z założeniami projektowymi, albo podnoszę sprawę na spotkaniu i obgadujemy czy nie trzeba zmienić specyfikacji. No ale jak ustalimy żeby zmienić, to jest to jawne i będzie mieć odzwierciedlenie np. w dokumentacji.

Bo tym są testy dla mnie, są odpowiedzią na pytanie:
czy można zdeployować aplikacje
czy aplikacja działa
czy aplikacja ma błędy

Żeby odpowiedzieć na Twoje pytania, wystarczą testy end-to-end.

Ale testy odpowiadają również na pytania:

  • jak użyć danego systemu / modułu / klasy / metody / funkcji
  • w jakim obszarze jest błąd, jeśli jest
  • czy kod działa zgodnie z przyjętymi założeniami / dokumentacją tudzież czy dokumentacja nie kłamie
edytowany 5x, ostatnio: Krolik
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
Krolik napisał(a):

Jaki zysk będziesz miał z tego że się wywalą w takiej sytuacji?

  • Mogą mnie uchronić przed potencjalną katastrofą w przyszłości. Świat nie jest idealny i testy nie są w stanie przetestować wszystkiego. Możliwe że ten wywalający się test w jakiejś implementacji tak naprawdę oznacza błąd również z punktu widzenia bizensu, ale testy wyższego poziomu go nie złapały. Przykładowo, drzewo miało być zrównoważone, okazało się że nie jest, ale poza tym działa i cała reszta jest happy. Testy end-to-end mogą tego nie wyłapać bo np. nie było dostatecznie dużo danych aby zauważyć wpływ na wydajność.

Tylko że jeśli przyjmiesz takie założenie, to tak na prawdę nie masz enkapsulacji w projekcie - wszystko jest zawsze widoczne i testowane. Według tej definicji każdy jeden stan i każde jedno pole powinno być ustalone i widoczne jak public. Nie ma prawa być żadnego pola private i metody private.

Off-topic

Dobrze, rozumiem, że z jakiegoś powodu masz uprzedzenie do TDD. To co widzę, to również to, że wiele razy spaliłeś się na subtelnych bugach w aplikacjach, (pewnie przez słabe testy), więc teraz doszedłeś do wniosku że lepiej złamać enkapsulację (czyli żeby wszystko było jawne, przegadane i do ustalenia), niż próbować ją respektować. Ma to sens, i rozumiem to. To jest dobry krok jesli wychodzimy z miejsca gdzie aplikacje ciągle nie działają.

Ale nie jest to ostateczny krok. Tzn, byłby ostateczny, gdyby ta aplikacja się nie rozwijała dalej, tzn. gdyby nie musiała mieć żadnych zmian. Wtedy tak jak jest jest okej.

Ale jeśli chcemy żeby ta aplikacja mogła się dalej rozwijać, i być edytowana; to są sposoby jak możnaby ją usprawnić, tak żeby była wyższej klasy, ogólnie była lepsza i tańsza do utrzymania. Próbowałem Ci opowiedzieć o sposobach jak można to osiągnąć, ale widzę że nie udało mi się Ciebie przekonać. Nie sądzę że ma to dalszy sens ta rozmowa. Szkoda, że mi się nie udało, bo pamiętam że sam kiedyś wyznawałem dokładnie takie samo podejście jak Ty - żeby przetestować wszystko, najmniejszy kawałek, żeby przez maleńkie zmiany testy failowały, prywatne czy nie. Ale nauczyłem się, że takie aplikacje są cieższe do utrzymania i rozwijania, i zrozumiałem że w wielu przypadkach odpowiednie schowanie implementacji, i skupienie się jedynie na zachowaniu aplikacji jest kluczem. @Krolik Jeśli będziesz chciał kiedyś wrócić do rozmowy, to obawiam się że bez konkretnego przypadku nie da rady.

Swoją drogą, nadal mi nie odpowiedziałeś @Krolik , zerknąłeś na mój przykład projektu https://github.com/t-regx/crlf odnośnie TDD?

Także dzięki za debatę.

edytowany 3x, ostatnio: Riddle
Zobacz pozostałe 11 komentarzy
KR
Tak, zapisałbym w innym miejscu a po upewnieniu się że jest zapisany bym go przeniósł.
Riddle
Dlatego taki test by zapewne nie powstał przy używnaniu TDD. Wydaje mi sie że to jest właśnie słabość TDD - koncentrujesz się na tych zachowaniach, które da się łatwo przetestować. - Powstałby, myślę że tutaj masz błędne przekonanie. Jak ktoś stosuje TDD to pisze testy pod cały kod - zarówno te przypadki które da się łatwo, jak i te które da się trudno przetestować. Jeśli ktoś tylko udaje TDD, to faktycznie napisze testy pod łatwe case'y, a trudne ominie - wtedy faktycznie masz rację, to się mija z celem. Ale jesli ktoś na prawdę praktykuje TDD to napisze testy pod..
Riddle
...wszystkie przypadki. Jak zaczynam pisać kod najpierw to nie myślę jak go będę testował, tylko myślę o tym aby dobrze działał., no ale w TDD właśnie dokładnie o to chodzi, żeby napisać kod aby dobrze działał - również o takich rzeczach jak zapisanie pliku w innym miejscu, i potem przeniesienie go. Jeśli dla Ciebie "poprawne działanie" w tym programie, to jest napierw zapisanie go w innym miejscu, a potem przeniesienie go, to właśnie test pod takie zachowanie powinien powstać najpierw.
KR
Tylko że motywacja do napisania trudnego testu najpierw do łatwego kodu, którego nawet nie mamy jest bardzo.... słaba?
Riddle
@Krolik: No zgadzam się - absolutnie motywacja jest słaba. Dlatego jest tak wiele mitów TDD, i tak wiele osób twierdzi że praktykuje TDD, podczas gdy tego nie robi. TDD to bardzo trudna umiejętność. Dlatego warto sobie wyrobić dyscyplinę w pisaniu testów z TDD, tak by ta motywacja do tego była większa.
KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:2964
1

Tylko że jeśli przyjmiesz takie założenie, to tak na prawdę nie masz enkapsulacji w projekcie - wszystko jest zawsze widoczne i testowane. Według tej definicji każdy jeden stan i każde jedno pole powinno być ustalone i widoczne jak public. Nie ma prawa być żadnego pola private i metody private.

Myślę, że to zbyt daleko posunięty wniosek. Mogę mieć w kodzie rzeczy prywatne widoczne tylko dla testów, a nadal prywatne dla reszty kodu. Zresztą, te wszystkie rzeczy jak enkapsulacja nie są same w sobie wartościami a jedynie narzędziami do osiągnięcia pewnego celu. Nie wszedzie musi być enkapsulacja.

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
Krolik napisał(a):

Tylko że jeśli przyjmiesz takie założenie, to tak na prawdę nie masz enkapsulacji w projekcie - wszystko jest zawsze widoczne i testowane. Według tej definicji każdy jeden stan i każde jedno pole powinno być ustalone i widoczne jak public. Nie ma prawa być żadnego pola private i metody private.

Myślę, że to zbyt daleko posunięty wniosek. Mogę mieć w kodzie rzeczy prywatne widoczne tylko dla testów, a nadal prywatne dla reszty kodu. Zresztą, te wszystkie rzeczy jak enkapsulacja nie są same w sobie wartościami a jedynie narzędziami do osiągnięcia pewnego celu. Nie wszedzie musi być enkapsulacja.

Jasne.

Tylko widzisz, z enkapsulacją jest tak, że ona ma same zalety i praktycznie żadnych wad.

Kod z enkapsulacją:

  • jest łatwiejszy do zmiany
  • łatwiejszy do zrozumienia
  • ma mniejszą szansę że cos zepsuje
  • zmiany w małej części kodu nie propagują zmian na cały system

Natomiast wada jedyna jaka jest, to taka że nie każdy umie ją zastosować. Tzn. ktoś nie umie napisać dobrych testów które respektują enkapsulację, więc złamie ją żeby napisać test. Lepsze takie testy niż żadne, no ale jednak to nie jest najlepsze co można zrobić.

Chodzi o to że nic nie tracisz dodając enkapsulację, a możesz zyskać. Nie ma więc powodu żeby jej nie dodać - poza tym że ktoś może nie umieć tego zrobić.

edytowany 1x, ostatnio: Riddle
AN
  • Rejestracja:prawie 11 lat
  • Ostatnio:5 minut
  • Postów:973
2

Problem w tym, że są nie trywialne problemy, które możesz zenkapsulować ale ciężko je rozwijać bez testów (ty byś miał jeden test, a Kroolik miałby kilka, które sprawdzają implementację). Jak by trzeba było poprawić coś to Kroolik to zrobi w 15 minut a ty 3 godziny


Zdalna praca dla Senior Python Developerów --> PW
Z4
  • Rejestracja:prawie 2 lata
  • Ostatnio:ponad rok
  • Postów:32
2

@Riddle - wszystko ma wady, a enkapsulacja nie jest tu żadnym wyjątkiem.

Enkapsulacja chowa danych, ale pociąga za sobą konieczność przygotowania specyficznych metod, które pośrednio będą przetwarzać te dane, ale nie jest to za darmo:

Pierwsza rzecz to niezależnie jakie masz dane to i tak musisz pisać nowe definicje, by te dane opakować. To tak jakby każdą myśl jaka zamierzasz w kodzie wyrazić wymagała przekuwania na nową definicję. To nie tylko przeczy ponownemu wykorzystaniu kodu, ale również prowadzi do powstania nadętego i zbyt formalnego podejścia, które zamiast rozjaśniać temat to robi coś wręcz przeciwnego. Weź otwórz sobie OWU dowolnej polisy, oni tam na początku wyprowadzają definicje, dość oczywistych pojęć, nie po to by ułatwić zrozumienie tekstu, ale żeby wykluczyć ogólne znaczenie, coś co każdy zna, w ten sposób utrudniają zrozumienie tekstu, bo czytając warunki umowy trzeba ciągle wracać do tych definicji, aby zajarzyć pod jakim względem ubezpieczyciel robi Cię w bambuko.

Co więcej kod, który piszesz później utrzymujesz i jak wiesz podlega on zmianom, a rzeczy specyficzne zmienność mają wpisaną w swoją definicję. Masz metodę X (dzisiaj ona wysyła maila i generuje raport, a za miesiąc ta sama może robić coś więcej lub coś mniej). Efekt jest taki, że o ile od środka możesz łatwo wprowadzać zmiany, o tyle z zewnątrz, ze strony czytelnika, który próbuje zrozumieć kod korzystający z wywołania metody prowadzi do trudności ze zrozumieniem, ponieważ trzeba skakać i regularnie wracać do definicji klas, aby zrozumieć co kod właściwie robi od środka.

Ostatnim minusem jaki znam jest fakt, że ukrywanie jest OK w przypadku, gdy ukrywasz coś nieistotnego i marginalnego. Enkapsulacja jest OK w przypadku klienta http, klienta pocztowego, kolekcji, plików, parsera itp, ale nie w przypadku logiki biznesowej. Jeśli zaczniesz ukrywać to co istotne, to osoba, która czyta kod i która chce zrozumieć co robi aplikacja to właściwie ma problem, bo musi znowu skakać i zaglądać do wszystkich definicji powiązanych z procedurą, by posklejać fakty do kupy, ale najgorzej jest jeśli musisz rozszerzyć kod i dostać się do tej istotnej informacji, wówczas abstrakcje jakie tworzysz zaczynają przeciekać i wszystko co bazowało na tym api trzeba poprawiać. Logika biznesowa nie jest marginalna i założenia w okół logiki mogą Ci się najszybciej popsuć, tzn. tam gdzie chciałeś coś ukryć, musisz po jakimś czasie odsłonić itp.

Najlepszą puentą dotyczącą enkapsulacji, nad którą zachęcam Cię byś trochę  pomyślał, jest jest cytat Alana Perlisa: It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. Jak myślisz co ten gościu miał na myśli? Postrzeliło go czy miał rację? :D

edytowany 3x, ostatnio: znowutosamo4
W0
It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. Jak myślisz co ten gościu miał na myśli? Postrzeliło go czy miał rację? - ja jednak wybiorę to drugie. Jeśli sto funkcji korzysta z jednej struktury to istnieje spora szansa, że masz do czynienia albo z czymś niesamowicie generycznym i przez to błędogennym (np. mapa), albo z jakimś god objectem.
Z4
Pomyśl jak o excelu, masz tam setki funkcji, których nie musisz od środka czytać ani zmieniać.
W0
Wolę programować w dowolnym, normalnym języku niż w Excelu.
Z4
Excel to nie język więc porównanie z językiem nie do końca trafione, ale z drugiej strony pisząc w excelu nie mutujesz danych. Czy Twój normalny język też tak ma? :D Jak dla mnie możesz wydziwiać, o gustach się nie dyskutuje, ale ja tego nie kupuje, bo to nie jest żaden argument. Generalnie oba podejścia mają trochę sensu i warto dozować. Podejście 10x10 jest lepsze, gdy musisz ograniczyć dostęp do zasobów, aby zapewnić spójność, wywiązanie się z postawionych ograniczeń itp, ale podejście 100 funkcji na liście powoduje orientacje na dane, wyrażenia coś jak w FP (..)
Z4
po prostu łatwiej jest to składać, a w końcowym rezultacie łatwiej czytać, bo wtedy wiesz, że poza obliczeniem wyniku nic niepożądanego nie ma miejsca w tle.
W0
Przestałeś pisać na temat. Nietrafione porównanie z Excelem to twój wymysł, a niemutowalność jest ortogonalna wobec zagadnienia liczby struktur danych.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
znowutosamo4 napisał(a):

Pierwsza rzecz to niezależnie jakie masz dane to i tak musisz pisać nowe definicje, by te dane opakować.

Tak.

To tak jakby każdą myśl jaka zamierzasz w kodzie wyrazić wymagała przekuwania na nową definicję. To nie tylko przeczy ponownemu wykorzystaniu kodu, ale również prowadzi do powstania nadętego i zbyt formalnego podejścia, które zamiast rozjaśniać temat to robi coś wręcz przeciwnego.

Niepoprawny wniosek - nie przeczy temu w żaden sposób, wręcz przeciwnie.

Weź otwórz sobie OWU dowolnej polisy, oni tam na początku wyprowadzają definicje, dość oczywistych pojęć, nie po to by ułatwić zrozumienie tekstu, ale żeby wykluczyć ogólne znaczenie, coś co każdy zna, w ten sposób utrudniają zrozumienie tekstu, bo czytając warunki umowy trzeba ciągle wracać do tych definicji, aby zajarzyć pod jakim względem ubezpieczyciel robi Cię w bambuko.

Nie muszę chyba mówić że wytwarzanie oprogramowania to jest coś innego niż czytanie polis? Argument nieadekwatny zuepłnie.

Co więcej kod, który piszesz później utrzymujesz i jak wiesz podlega on zmianom, a rzeczy specyficzne zmienność mają wpisaną w swoją definicję. Masz metodę X (dzisiaj ona wysyła maila i generuje raport, a za miesiąc ta sama może robić coś więcej lub coś mniej). Efekt jest taki, że o ile od środka możesz łatwo wprowadzać zmiany, o tyle z zewnątrz, ze strony czytelnika, który próbuje zrozumieć kod korzystający z wywołania metody prowadzi do trudności ze zrozumieniem, ponieważ trzeba skakać i regularnie wracać do definicji klas, aby zrozumieć co kod właściwie robi od środka.

Z zewnątrz też jest to banalne do zrobienia, możesz po prostu napisać nową funkcję, a rzeczy wspólne wydzielić - jeśli już nie są wydzielone.

Ostatnim minusem jaki znam jest fakt, że ukrywanie jest OK w przypadku, gdy ukrywasz coś nieistotnego i marginalnego. Enkapsulacja jest OK w przypadku klienta http, klienta pocztowego, kolekcji, plików, parsera itp, ale nie w przypadku logiki biznesowej. Jeśli zaczniesz ukrywać to co istotne, to osoba, która czyta kod i która chce zrozumieć co robi aplikacja to właściwie ma problem, bo musi znowu skakać i zaglądać do wszystkich definicji powiązanych z procedurą, by posklejać fakty do kupy, ale najgorzej jest jeśli musisz rozszerzyć kod i dostać się do tej istotnej informacji, wówczas abstrakcje jakie tworzysz zaczynają przeciekać i wszystko co bazowało na tym api trzeba poprawiać. Logika biznesowa nie jest marginalna i założenia w okół logiki mogą Ci się najszybciej popsuć, tzn. tam gdzie chciałeś coś ukryć, musisz po jakimś czasie odsłonić itp.

To nie jest argument przeciwko enkapsulacji, tylko przeciwko niepoprawnej enkapsulacji.

W enkapsulacji chodzi o to żeby schować rzeczy nieistotne, a uwidocznić istotne - po prostu.

Najlepszą puentą dotyczącą enkapsulacji, nad którą zachęcam Cię byś trochę  pomyślał, jest jest cytat Alana Perlisa: It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. Jak myślisz co ten gościu miał na myśli? Postrzeliło go czy miał rację? :D

Oba mogą być okej, zależnie od przypadku.

edytowany 1x, ostatnio: Riddle
Z4
Niepoprawny wniosek - nie przeczy temu w żaden sposób, wręcz przeciwnie. - czyli robisz enkapsulacje i to nie pociąga za sobą tworzenia definicji? jak to robisz? xD
Riddle
Przeczytaj mój post jeszcze raz.
Z4
Nie muszę chyba mówić że wytwarzanie oprogramowania to jest coś innego niż czytanie polis? Argument nieadekwatny zuepłnie. jak już logicznie zauważyłeś polisy to nie kod, ale analogia, która ma wspólne podłoże - treść do czytania przez człowieka, podlegająca utrzymaniu i w efekcie zmianom.
Z4
@Riddle: ale w zasadzie z Tobą nie da się dyskutować, bo Ty wszystko bronisz X ok, ale "niepoprawne X" już nie. Jak to teraz odbijesz? ;D Stwierdzeniem, że to uwaga nie na temat?
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
znowutosamo4 napisał(a):

@Riddle: ale w zasadzie z Tobą nie da się dyskutować, bo Ty wszystko bronisz X ok, ale "niepoprawne X" już nie. Jak to teraz odbijesz? ;D Stwierdzeniem, że to uwaga nie na temat?

Podałeś trzy argumenty, za tym że enkapsulacja ma wady w poście: Jak przetestować metodę która zmienia prywatne pole?

Podsumowując je:

  • pierwszy, to że nowe definicje przeszkadzają reużywaniu elementów:
    • to jest po prostu nie prawda
  • drugi, to analogia do polis
    • to jest po prostu nieadekwatny argument
  • trzeci, to ilustracja tego że niepoprawnie zrobiona enkapsulacja jest szkodliwa
    • z czym się zgadzam. ale to nie jest argument za tym że enkapsulacja jest zła, tylko to że jeśli ktoś ją nieudolnie spróbuje zrobić to osiągnie słabe efekty - i to jest prawda
Z4
@Riddle spoko, nie przejmuj się, ten poboczny wątek, patrząc na nie tak dawną dyskusję o DI to i tak nie miał zbytnio szans na rozwiniecie. Generalnie warto od czasu do czasu spojrzeć na coś z innej perspektywy. Jeśli to zbyt wiele to trudno, będę niezrozumiany. Dla mnie taka właśnie zamknięta postawa przeczy wartościowym dyskusjom. No chyba, że celem dyskusji jest pokazanie najpopularniejszej opinii bądź zniszczenie wszystko innego co jest od niej różne.
Z4
Możemy się sprzeczać, ale jaki to ma sens jeśli nawet nie porusza Cię fakt, że widzisz jakieś rozwiązanie/podejście w superlatywach nie dostrzegając przy tym jakichkolwiek wad. W IT wszystko ma swoje dobre i słabe strony. Nieznajomość zwłaszcza tych drugich świadczy tylko o niewiedzy. Jeśli Twoje argumentacja (podkreślam: w przypadku wad) o enkapsulacji sprowadza się do tego, że można to zrobić dobrze, bądź źle, nie dając żadnych konkretów to w ten sposób dajesz mi jedynie do zrozumienia, że ignorancję masz rozwiniętą na wysokim poziomie.
WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 17 godzin
  • Postów:5108
2

@Riddle

No okej, Ty sobie to nazywasz podejście "teoretyka", ale ja takie podejście stosuję w swoich komercyjnych projektach od lat. Więc dla mnie to nie jest nic "teoretycznego".

ale co z tego, jeżeli nadal jest to inny kontekst?

piszecie inny soft, oparty o inne założenia, w innych firmach, technologiach i teamach


Najważniejszą rzeczą w teście, wymaganiem jest: test failuje gdy software nie odzwierciedla specyfikacji, tyle.

Nie ma nic ważniejszego.

I teraz wchodzi część inżynieryjna - kompromisy oraz wymagania. Tak wymagania, bo kod piszę się pod wymagania.

Czy testy są szybkie?

Czy testy wpływają na implementacje? (to jest w ogóle ciekawe, bo co to znaczy że piszesz testowalny kod? no to, że zaprojektowałeś go w sposób testowalny, a więc testy wpłynęły już na impl :))

Jak precyzyjnie lokalizują problem?

Jak problematyczne utrzymanie jest testów?

Czy generują "false" wyniki?

Ile czasu pochłania ich pisanie

i pewnie wiele więcej...

To teraz wszystko rozchodzi się o to, jakie wymagania lub wartości chcesz uzyskać

bakunet
  • Rejestracja:prawie 8 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Polska
  • Postów:1596
0

Ok, wracając do mojego podjejścia i wątku, TDD nie uprawiam. Może jestem na to za cienki, nie wiem. Zabierając się za projekt od podstaw często jeszcze nie wiem na jakie ograniczenia trafię, jakie technologie wykorzystam, w jakim stopniu będę musiał docelowo ograniczyć swoje plany (czas realizacji i wydajność) oraz jakie funkcjonalności / modele danych będę chciał dorzucić.

Najczęściej zabierając się za nowy projekt od podstaw to obecnie najpierw piszę spike w bardzo wąskim zakresie funkcjonaności żeby sprawdzić czy się uda i jakich technologii użyję, później piszę słabo / średnio testowalny kod dokładając do niego funkcjonalności lub usuwając co nie ma racji bytu, z czego powstaje w serwisach spaghetti, póki wszystko rośnie. Na koniec refaktoruję całość tak, żeby w serwisach wyższego rzędu nie było logiki lub było jej mało. Ostatecznie całą logikę próbuję zepchnąć do serwisów które nie mają żadnych zależności lub mają ich mało.

Z tego wątku przykład odczytuje / przypisuje wartość prywatnemu polu. Zakłądając że w metodach Get i Set jest jakaś logika, których wynik będzie rzutował na funkcjonowanie całego programu i poprawność otrzymywanych wyników, wciąż nie jestem przekonany do tego żeby ich nie testować. Choć chyba po prostu powinienem zepchnąć logikę jeszcze niżej i tyle, lub zepchnąć niżej i mockować wartość prywatnego pola. I zasanawiam się czy i co byłoby niepoprawnego w takim podejściu?

Przykład z wątku jest trywialny i łatwo jest ocenić bez testowania czy metody działają poprawnie. Aczkolwiek są też metody obliczeniowe / algorytmiczne które potrafią namieszać w otrzymywanych wynikach i bym nie zasnął spokojnie bez pewności że wypluwają poprawne wyniki. Tak więc bez ich testowania wydaje mi się że obejść się nie może.

Odpowiedź @MarekR22 oznaczyłem jako rozwiązanie mojego problemu i problemów w przyszłości. W miarę możlwości spróbuję stosować podejście TDD. Liczę na to, że kod będzie lepiej przemyślany. Choć trochę kłócą się jego słowa Celem testów jest sprawdzanie zewnętrznie widocznej funkcjonalności, a nie detali implementacyjnych. z tym co do tej pory myślałem o testach. Czy implementacja nie wpływa na funkcjonalność?

Zgadzam się w 100% z @Riddle i innymi osobami udzielającymi się w tym wątku, że wszystko co prywatne powinno pozostac niewidoczne dla innych klas. Choć wciąż nie do końca umiem się zgodzić z tym, żeby testować najwyższy poziom tylko (funkcjonalność), bo tak rozumiem wypowiedzi. Sprostujcie mnie jak się mylę. Tak jak pisał @obscurity , Czyli potrzebujesz prawdopodobnie testu do klasy która korzysta z tej klasy, wtedy pokrywasz testami je obie , jeśli klasa wyższego poziomu korzysta z tej klasy i ją mockuję wartościami z czapy, to co mi da taki test, jeśli klasa od której jest zależna zwraca logicznie niepoprawne wyniki? Czy to co pisał @Krolik , jeśli w teście korzystam z read i write, to czy znalezienie błędu w jednym z nich nie będzie trudniejsze bez przetestowania ich z osobna?

MarekR22
TDD jest łatwe. Głowny problem z TDD to utrzymanie własnej dyscypliny.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
bakunet napisał(a):

Z tego wątku przykład odczytuje / przypisuje wartość prywatnemu polu. Zakłądając że w metodach Get i Set jest jakaś logika, których wynik będzie rzutował na funkcjonowanie całego programu i poprawność otrzymywanych wyników, wciąż nie jestem przekonany do tego żeby ich nie testować. Choć chyba po prostu powinienem zepchnąć logikę jeszcze niżej i tyle, lub zepchnąć niżej i mockować wartość prywatnego pola. I zasanawiam się czy i co byłoby niepoprawnego w takim podejściu?

Wręcz odwrotnie.

Logikę powinieneś wynieść wyżej.

bakunet
  • Rejestracja:prawie 8 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Polska
  • Postów:1596
0
Riddle napisał(a):

Wręcz odwrotnie.

Logikę powinieneś wynieść wyżej.

Ale czemu? Chyba łatwiej przetestować klasę bez zależności. Nie piszę że nie, tylko próbuję zrozumieć.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Lokalizacja:Wrocław
0
bakunet napisał(a):

Z tego wątku przykład odczytuje / przypisuje wartość prywatnemu polu. Zakłądając że w metodach Get i Set jest jakaś logika, których wynik będzie rzutował na funkcjonowanie całego programu i poprawność otrzymywanych wyników, wciąż nie jestem przekonany do tego żeby ich nie testować. Choć chyba po prostu powinienem zepchnąć logikę jeszcze niżej i tyle, lub zepchnąć niżej i mockować wartość prywatnego pola. I zasanawiam się czy i co byłoby niepoprawnego w takim podejściu?

No skoro ta logika jest taka ważna, że rzutuje na poprawność całości, to powinna być ona wydzielona do oddzielnych klas/metod, i oddzielnie porządnie przetestowana.

AK
  • Rejestracja:ponad 6 lat
  • Ostatnio:2 dni
  • Postów:3561
2

Nie wiem, czy mi czegoś nie umknęło w tym mega wątku.

ja bym pytanie przeredagował:
testowanie klas posiadajacych stan (wszyscy wiemy, ze najlepiej immutable)

Zmiana zmiennej prywatnej - gdyby to była tylko ona - nie jest/nie musi być zmianą stanu, jeśli tego nic nie ujawnia na zewnątrz.
To "stan" czy "zmiana stanu" jest jakością podlegającą testom, a nie jego doraźna implementacja


Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 17 godzin
  • Postów:5108
0

@AnyKtokolwiek:

jeśli tego nic nie ujawnia na zewnątrz.

niestety nie jest to wykonalne. niezależnie od tego jakie podejście do programowania zastosujesz - oop, fp, etc., to i tak zawsze będziesz leakował

edytowany 1x, ostatnio: WeiXiao
AK
  • Rejestracja:ponad 6 lat
  • Ostatnio:2 dni
  • Postów:3561
0
WeiXiao napisał(a):

@AnyKtokolwiek:

jeśli tego nic nie ujawnia na zewnątrz.

niestety nie jest to wykonalne. niezależnie od tego jakie podejście do programowania zastosujesz - oop, fp, etc., to i tak zawsze będziesz leakował

Mi Bloch na gruncie Javy uświadomił.
Object ma integera jako cache do obliczenia hasha.

I np z konkretnych klas, dziedzczący z niego String, jest w oczywisty sposób immutable, ale zmienia wewn zmienną w momencie pierwszego wywoałania hashCode() Przestaje być immutable? No nie ...


Bo C to najlepszy język, każdy uczeń ci to powie
WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 17 godzin
  • Postów:5108
0

@AnyKtokolwiek:

Mi Bloch na gruncie Javy uświadomił.

wut

I np z konkretnych klas, dziedzczący z niego String

wut

anyway

chodziło mi o prąd, temperaturę, zużycie cpu itd.

edytowany 1x, ostatnio: WeiXiao
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:40 minut
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2
WeiXiao napisał(a):

chodziło mi o prąd, temperaturę, zużycie cpu itd.

Wut...

To co wymieniasz nie ma wiele wsólnego z immutable.
Zużycie cpu itp. można traktować jak impure, ale i tak jest to mocno akademicka, niepraktyczna definicja.
(Bo co daje infotmacja, że poza pewnymi językami wszystkue funkcje są impure? ).


jeden i pół terabajta powinno wystarczyć każdemu
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10055
0
AnyKtokolwiek napisał(a):
WeiXiao napisał(a):

@AnyKtokolwiek:

jeśli tego nic nie ujawnia na zewnątrz.

niestety nie jest to wykonalne. niezależnie od tego jakie podejście do programowania zastosujesz - oop, fp, etc., to i tak zawsze będziesz leakował

Mi Bloch na gruncie Javy uświadomił.
Object ma integera jako cache do obliczenia hasha.

I np z konkretnych klas, dziedzczący z niego String, jest w oczywisty sposób immutable, ale zmienia wewn zmienną w momencie pierwszego wywoałania hashCode() Przestaje być immutable? No nie ...

No przestaje.

Np na potrzeby wątków. Jeśli masz obiekt który na prawdę jest immutable to możesz go do woli przekazywać między wątkami bez konieczności synchronizacji. Jeśli masz obiekt który "wygląda" na immutable, ale pod spodem zmienia stan, to nadal musisz mieć synchronizację tego.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:40 minut
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
0
Riddle napisał(a):

No przestaje.

Np na potrzeby wątków. Jeśli masz obiekt który na prawdę jest immutable to możesz go do woli przekazywać między wątkami bez konieczności synchronizacji. Jeśli masz obiekt który "wygląda" na immutable, ale pod spodem zmienia stan, to nadal musisz mieć synchronizację tego.

Teraz tylko jeszcze pozostaje wytłumaczyć to gościom odpowiedzialnym za String w javie. Bo ewidentnie zrypali.

Btw. tak naprawde to serio ciekawy problem, chyba teoretycznie jest możliwe dostanie złego hashCode w środowisku wielowątkowym....


jeden i pół terabajta powinno wystarczyć każdemu
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)