Wątek przeniesiony 2023-12-02 12:24 z Java przez Riddle.

W jakim pakiecie trzymać testy o uprawnieniach do usuwania modeli?

0

Testy typu "tylko admin może usunąć film" powinienem trzymać w module/pakiecie "films" czy "users/security"?

0

Nie ma na to jednoznacznej odpowiedzi.

Ja bym chyba wsadził do tego pakietu w którym jest mniej testów, tak żeby zachować proporcje. Ale możesz się kierować jakimi kryteriami w sumie chcesz.

0

Struktura testów taka sama jak struktura src.

1
musrus napisał(a):

Struktura testów taka sama jak struktura src.

Bój się boga.

Podobne struktury testów i kodu źródłowego zachęca do ciasnych powiazań między testami i kodem, a to jest nieporzadane.

1

tl;dr; Nie kwestionuję testów jednostkowych, kwestionuje Twoje rozumienie pojęcia "test jednostkowy".

musrus napisał(a):

@Riddle: negujesz zatem sensowność unit testów? Czy dobrze to rozumiem? Negujesz podejście z piramidą testów, gdzie unity są podstawą i jest ich najwięcej?
Chwalisz się znajomością extreme programming i TDD, a tutaj negujesz tak jakby sam sobie lub ja czegoś nie rozumiem

Zależy czym dla Ciebie są unit testy, bo definicja tego słowa jest dosyć ruchoma.

Jeśli mówimy o "unit testach", w rozumieniu jednostki takiej jaką wymienił Kent Beck w swojej książce "TDD by Example", w której jednostką jest spójny moduł lub zbiór modułów - to to jest super podejście, i tak się powinno robić.

Jeśli "unit test" to ma być test funkcji lub klasy, to to jest bezwartościowy test, i takich testów sensowność będę negował. Wygląda na to że właśnie takie rozumienie "unit testów" wyznajesz, i takie testy mają wartość bardzo słabą.

some_ONE napisał(a):

@Riddle: Bardzo mało dają takie testy Dają tyle, że wiem czy dany mały fragment kodu działa poprawnie czy nie.

Tak i nie. "Dają tyle, że wiem czy fragment kodu działa poprawnie" - z tym "poprawnie" to bym uważał. Test takiego helpera do stringów to jest zbyt wąski scope, żeby dać jakąś mieżalną wartość. Owszem, zgadzam się że napisanie jej jest szybkie, ale wartość z tego też jest niezwykle mała. Ale jakaś jest, zgoda.

Tylko że oprócz tej malutkiej wartości, jest też wada - ciasne przywiązanie testów do implementacji - i to jest mój główny argument przeciwko takim testom. Testy które testują szersze moduły są lepsze.

some_ONE napisał(a):

To czy ktoś to uważa to za przydatne czy nie to już raczej ocena subiektywna.

O, i tu się mylisz, bo są dane które pokazują że testy o szerzym scope'ie powodują mnie bugów w całości życia aplikacji.

some_ONE napisał(a):

Koszt napisania takich testów jest praktycznie żaden, a jak dla mnie mają wartość, bo szybko widać co przestało działać - widzę że nie działa coś w klasie X, a nie tylko, że test interfejsu modułu Y, który dotyka 20 takich klas się wywalił. I nijak nie stoi to w sprzeczności z tym, że obok powinny być testy które testują interfejs wejściowy.

Tak, i tym samym dodajesz bardzo ciastne połączenie kodu produkcyjnego z testami, przez co późniejsza zmiana tego kodu lub refaktoryzacja jest trudniejsza. Więc oszczędzisz kilka minut pisząc taki test, ale stracisz godziny jak nie więcej próbując edytować swój kod w przyszłośc, nie mówiąć już o bugach które przeczmychną przez takie testy.

Pyxis napisał(a):

@Riddle: Twoje podejście łatwo może przeoczyć przypadki brzegowe.

Bez przykładu ciężko o tym mówić.

musrus napisał(a):

Chyba trochę wchodzimy na wojnę testy jednostkowe vs integracyjne. To chyba wojna bez sensu, gdyż będę upierał się przy piramidzie, gdzie jednostkowe powinny być podstawą, potem integracyjne łączące kilka komponentów/ modułów, gdzie ich ilość moze byc mniejsza bo logicznie testuje jedynie powiązania a nie logikę pod spodem przez co komplet takich testów jest o wiele bardziej wydajy.

Tylko że funkcja i klasa to nie jest jednostka. Tutaj się mylisz.

musrus napisał(a):

Poza tym wykonanie testów integracyjnych, które przetestują wszystkie możliwe przypadki jest bardzo niewydajne lub też niemozliwe do wykonania (w przeciwieństwie do jednostkowych).

Tutaj też się mylisz, bo testy o szerokim zakresie nie są wcale wolniejsze albo niewydajne.

Wydaje mi się że w Twojej głowie masz takie rozróznienie:

  • "Testy jednostkowe" - testy pojedynczej klasy lub funkcji
  • "Testy integracyjne" - testy stawiające bazę i server

No i to się trochę mija z prawdą.

Bo prawdą jest że:

  • Testy jednostkowe to powinny być testy całego modułu
  • Testy integracyjne wcale nie muszą podnosić bazy, servera i być wolne, mogą się wykonywać tak samo szybko jak jednostkowe

Zgodzę się jednak z Twoim zdaniem tutaj: gdyż będę upierał się przy piramidzie, gdzie jednostkowe powinny być podstawą, potem integracyjne łączące kilka komponentów/ modułów. To jest prawda. Absolutnie, testy jednostkowe powinny być podstawą, a testy integracyjne powinny testować połączenie modułów. Tylko że jednostką tutaj nie jest pojedyncza klasa czy funkcja. Jednostką właśnie jest moduł. I test który testuje np utilsa jednego, to nie jest test jednostkowy. To jest jakiś frankestain, abominacja. Jeśli ten util jest użyty np w parserze tekstu, to test pokrywający ten parser powiniene przetestowac tego utilsa - utils nie powinien mieć oddzielnego testu, bo to tylko przywiązuje testy do implementacji i spowalnia rozwój projektu.

0

No i teraz wchodzimy na rejony co jest faktycznie testem jednostkowym.
Szczerze powiedziawszy chyba nie ma sensu dyskutować nad poprawną definicją. Ważniejsze jest to, co w danym oprogramowaniu będzie jednostką. Masz rację Riddle, że w niektórych przypadkach kilka klas np. Potraktujesz jako jednostkę, ale to samo się ma i w drugą stronę, w niektórych sytuacjach jednostką będzie po prostu metoda lub jedna mała klasa. To jest na prawdę ciężko określić bez przykładów, jak wspomniałeś.
Także podsumowując wydaje mi się, że myślisz tak samo lub bardzo podobnie, jedynie nie rozumiemy się co, kto ma do przekazania. Myślę, że na poziomie kodu byśmy się dogadali ;)

I teraz, jeśli wyjdziemy z założenia, że test jednostkowy to złożenie kilku elementów to oczywistym jest, że struktura testów będzie inna niż src. Z tym, że gdzie to umieścić i jak nazwać będzie mocno zależny od kontekstu kodu. Nie sposób jednoznacznie określić, że tu i tu będzie najlepiej, bez spojrzenia na kod.

0
Riddle napisał(a):

Tak i nie. "Dają tyle, że wiem czy fragment kodu działa poprawnie" - z tym "poprawnie" to bym uważał.

To samo można powiedzieć o każdych testach :P

Riddle napisał(a):

Tylko że oprócz tej malutkiej wartości, jest też wada - ciasne przywiązanie testów do implementacji - i to jest mój główny argument przeciwko takim testom. Testy które testują szersze moduły są lepsze.

Ale dlaczego przywiązanie testów do implementacji? Jak dla mnie takie testy nie muszą o tym świadczyć.
Pisałem kiedyś parser jakiegoś prostego własnościowego protokołu, który wykorzystywany był przez urządzenia IoT. Przyjmowaliśmy dane i trzeba było zdekodować ten ich protokół. No to napisałem sobie ten parser - dość prosta sprawa, pewnie w granicach kilkuset linii kodu i zrobiłem testy do niego.

I teraz pytanie jakie tutaj mam przywiązanie testów do implementacji?
Mogę refaktorować cały moduł, który korzystał z parsera i nijak to nie wpływa na te testy.
No ok, jak tak zrefaktoruję, że proces przyjmowania danych przestanie korzystać z parsera to testy parsera staną się bezużyteczne. Ale w ten sam sposób testy interfejsu modułu mogą się zdezaktualizować jak zrobię taki refaktor, że endpointy przestaną wywoływać ten moduł.

Riddle napisał(a):

O, i tu się mylisz, bo są dane które pokazują że testy o szerzym scope'ie powodują mnie bugów w całości życia aplikacji.

A te o mniejszym scopie powodują więcej bugów w całości życia aplikacji?
Bo to, że jedne testy mogą być bardziej przydatne od drugich, jeszcze nie znaczy że te drugie są bezużyteczne :P

Riddle napisał(a):

Tak, i tym samym dodajesz bardzo ciastne połączenie kodu produkcyjnego z testami, przez co późniejsza zmiana tego kodu lub refaktoryzacja jest trudniejsza. Więc oszczędzisz kilka minut pisząc taki test, ale stracisz godziny jak nie więcej próbując edytować swój kod w przyszłośc, nie mówiąć już o bugach które przeczmychną przez takie testy.

Nie muszę mieć bardzo ciasnego połączenia kodu produkcyjnego z testami - albo nie widzę dlaczego każde testy mniejszych kawałków miały by o tym świadczyć - poruszone wyżej w odpowiedzi na inny fragment.

1
some_ONE napisał(a):

Nie muszę mieć bardzo ciasnego połączenia kodu produkcyjnego z testami - albo nie widzę dlaczego każde testy mniejszych kawałków miały by o tym świadczyć - poruszone wyżej w odpowiedzi na inny fragment.

Dlatego, że testy wiedzą wtedy o tych małych kawałkach. Ten mały kawałek, powinno da się móc zmienić, zrefaktorowac, inlineować, renamować albo nawet usunąć i zastąpić go czymś zupełnie innym, tak żeby testy nie wymagały żadnej zmiany.

Te małe kawałki powinny być przetestowane, owszem - ale nie przez dedykowany test do nich, tylko za pośrednictwem innego testu, o szerszym scope'ie.

Innymi słowy, możesz mieć helpera StringHelper którego użyjesz w swoim parserze. Ale nie powinno być testu StringHelperTest (no chyba że faktycznie ten StringHelper jest interfejsem Twojej aplikacji, np jakbyś robił bibliotekę jak Guava - co raczej nie jest casem tutaj).

0
musrus napisał(a):

Masz rację Riddle, że w niektórych przypadkach kilka klas np. Potraktujesz jako jednostkę, ale to samo się ma i w drugą stronę, w niektórych sytuacjach jednostką będzie po prostu metoda lub jedna mała klasa.

Akurat na to Ci mogę odpowiedzieć bez przykładu. Jak masz test który testuje jedną klasę, to raczej (na 99%) to jest złe wyjście. Testy powinny testować zachowanie, jakąś jedną odpowiedzialność, jakiś feature, jakieś działanie. Szablon w stylu "jedna klasa" == "jeden test" (albo co gorsza "jedna funkcja" == "jeden test"), jest szkodliwy (poza nielicznymi wyjątkami).

0
Riddle napisał(a):

Innymi słowy, możesz mieć helpera StringHelper którego użyjesz w swoim parserze. Ale nie powinno być testu StringHelperTest (no chyba że faktycznie ten StringHelper jest interfejsem Twojej aplikacji, np jakbyś robił bibliotekę jak Guava - co raczej nie jest casem tutaj).

Może rzeczywiście ten utils to nie był najlepszy przykład, ale wydaje mi się że granica tutaj jest dość płynna.
Parser był elementem modułu przyjmującego dane, czy w takim razie nie powinienem mieć w ogóle testów parsera i wszystko testować przez interfejs modułu?

Mógłbym tak zrobić, ale widziałem wartość w napisaniu testów samego parsera - czy slusznie czy nie - to już inna sprawa :D

0
some_ONE napisał(a):
Riddle napisał(a):

Innymi słowy, możesz mieć helpera StringHelper którego użyjesz w swoim parserze. Ale nie powinno być testu StringHelperTest (no chyba że faktycznie ten StringHelper jest interfejsem Twojej aplikacji, np jakbyś robił bibliotekę jak Guava - co raczej nie jest casem tutaj).

Może rzeczywiście ten utils to nie był najlepszy przykład, ale wydaje mi się że granica tutaj jest dość płynna.
Parser był elementem modułu przyjmującego dane, czy w takim razie nie powinienem mieć w ogóle testów parsera i wszystko testować przez interfejs modułu?

To zależy od tego czy parser to jest odpowiedzialność sama w sobie, czy jest tylko szczegółem implementacyjnym innego modułu. Jeśli jest szczegółem implementacyjnym, to tak - nie powinieneś mieć testów pod niego konkretnie, i powinieneś wszystko testować przez interfejs modułu. Jeśli jednak parser jest odpowiedzialnością samą w sobie, to powinieneś mieć testy specjalnie pod niego, ale tylko pod interfejs tego parsera.

Dla przykładu:

  • Na forum korzystamy z parsera markdown do postów, i to jest osobna odpowiedzialność, dlatego że użytkownicy z niej korzystają, więc testy powinny być pod parser.

    Jeśli to się się zmieni (np markdown na rts) - to to jest bug.

  • Ale, z drugiej strony w bazie trzymamy czasem informacje o użytkownikach w JSON'ie jako formacie serializacji. I to już nie jest osobna odpowiedzialność, bo to jest tylko szczegół zapisu do bazy, i nie jest widziany z zewnątrz, więc gdybym napisał parser JSON'a tutaj, to nie powinno być testów pod to.

    Jeśli to się zmieni (np z json na xml) - to to nie jest bug, tylko refactor.

To co się zgodzę że jest płynne, to narysowanie granicy gdzie się zaczyna jeden moduł, a gdzie kończy drugi. Ale kiedy to już mamy, to decyzja o tym gdzie pisać test jest prosta - pisz testy pod interfejs modułu.

Dlatego warto zaczynać programowanie od testów, a nie od kodu.

Żeby być dobrym programistą i pisać dobre testy, musisz posiąść pewną bardzo istotną umiejętność - rozróżnianie rzeczy ważnych od nieważnych. Który kod jest celem samym w sobie (konkretnie interfejs aplikacji), a które rzeczy są szczegółem implementacyjnym, i są jedynie środkiem do celu. Testy należy pisać pod to pierwsze, i za wszelką cenę unikać testów pod to drugie. Testy pod interfejs i zachowanie aplikacji są bardzo pomocne, testy pod szczegóły implementacyjne w niczym nie pomagają i przeszkadzają w rozwoju aplikacji.

I jak testy wyglądają podobnie do kodu (mają podobną strukturę), to często to jest symptom testów pod implementację.

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