Czy jest sens testować delegację?

0

Mam sobie bardzo OOP kod. Mam też około 15 adapterów, które biorą klasy jakichś libek i nadają im interfejsy javowe. I teraz ich jedyna odpowiedzialność to jest przyjąć parametry i wywołać metodę delegata zwracając wynik - zwykły adapter.

Tylko testowanie takich adapterów jest dosyć mozolne, przez 25 minut napisałem test tylko do jednego. Chodzi o to że nie wystarczy wsadzić stringa 'Foo' i wyciągnąć 'Bar' (tzn, tak by było gdyby parameterm/return-type'm tej metody był String) tylko trzeba tworzyć mocki parameterów wejściowych, i wkładać fabtyki żeby się upewnić czy wychodzi dobra instancja.

No właśnie, niby nie zaszkodzi - ale kto by wszedł do adaptera i usunął albo podmienił kod - w klasie w której nie ma logiki?

No i nie wiem - testować te adaptery czy nie?

Logika wygląda mniej więcej tak

class LibSupplierAdapter<T> implements java.util.Supplier<T> {
    private final org.lib.Supplier<T> supplier;

    public T get() {
        return supplier.get(); // No kto tu przyjdzie i to zmieni?
    }
}
1

Nie.
Szczególnie jeśli org.lib.Supplier jest generyczny (u Ciebie nie, ale strzelam, że to pomyłka przy wklepywaniu tu).
Zepsucie tego w javie jest możliwe, ale na tyle nienaturalne, że bym odpuścił.

Takie rzeczy są testowane przy okazji testowania innych funkcji.
Warto, żeby coverage raz przeleciał.

Z drugiej strony IMO testowanie tego jest tak trywialne, że w zasadzie to można sobie z nudów zrobić. Ale potrzeby nie ma.
Podobnie w kotlinie nie testuje czy data class działają itp.

0
jarekr000000 napisał(a):

Podobnie w kotlinie nie testuje czy data class działają itp.

No ale data klasy nie mają faktycznie żadnej logiki. A adaptery, czy to delegacja czy nie to te 1-3 linijki można by klepnąć.

0

Ja bym nie testował, można ten czas spędzić lepiej.

0
Afish napisał(a):

Ja bym nie testował, można ten czas spędzić lepiej.

Zadając to pytanie bardziej spodziewałbym się jakichś argumentów za/przeciw, niż opinii.

1

No jeżeli dla Ciebie „marnowanie czasu” nie jest argumentem, to nie dojdziemy do konkluzji. Te testy nie dodają nic, jeżeli ktoś miałby tam napsuć, to powinno się to wyłapać innymi testami i przeglądem kodu.

0
Afish napisał(a):

No jeżeli dla Ciebie „marnowanie czasu” nie jest argumentem, to nie dojdziemy do konkluzji.

Nie powiedziałeś czemu testowanie tego to miałoby być marnowanie czasu - no ok, "możnaby poświęcić na coś innego". Jeden rabin powie tak, drugi rabin powie że w ogóle testy nie mają sensu bo można go poświęcić inaczej.

Ja się spodziewałem argumentów merytorycznych.

Te testy nie dodają nic, jeżeli ktoś miałby tam napsuć, to powinno się to wyłapać innymi testami i przeglądem kodu.

Czemu nie dodają nic? Gdyby nie dodawały nic - to uwierz - nie poświęciłbym 25 minut na pisanie ich. Te testy testują:

  • To czy adaptery poprawnie oddelegowują zachowanie/parametry/return'y do swoich zależności. Faktycznie, czasem ta delegacja jest bardzo prosta jak w moim przykładzie. Nie znaczy to że nie testują "nic".

Może zredaguję pytanie: Testy testują bardzo mało. Czy to "mało" jest warte swojego czasu i dlaczego?

to powinno się to wyłapać innymi testami i przeglądem kodu.

Może klasy JsonParser też nie będę testował, bo przecież test do HttpClient mu naklepie coverage?

to powinno się to wyłapać innymi testami i przeglądem kodu.

Nie po to piszę testy żeby wyłapywać bugi przeglądaniem kodu.

0
TomRiddle napisał(a):

Czemu nie dodają nic? Gdyby nie dodawały nic - to uwierz - nie poświęciłbym 25 minut na pisanie ich. Te testy testują:

  • To czy adaptery poprawnie oddelegowują zachowanie/parametry/return'y do swoich zależności. Faktycznie, czasem ta delegacja jest bardzo prosta jak w moim przykładzie. Nie znaczy to że nie testują "nic".

A łapiesz tę delegację gdzieś indziej? Jeżeli nie, to wtedy taki test jest przydatny, ale jeżeli łapiesz (a uważam, że powinieneś), to wtedy ten test nic nie daje. „Nie testuje nic” w sensie „nie testuje nic, co już nie byłoby przetestowane”.

Może zredaguję pytanie: Testy testują bardzo mało. Czy to "mało" jest warte swojego czasu i dlaczego?

Zależy od innych testów, które masz. Jeżeli te inne wyłapią tę interakcję, to moim zdaniem pisanie testów do adapterów jest stratą czasu. Ale jeżeli tych innych nie masz, to może warto byłoby się skupić na tamtych, zamiast testować proste delegacje.

Może klasy JsonParser też nie będę testował, bo przecież test do HttpClient mu naklepie coverage?

Coverage a poprawność to dwie różne sprawy. Poza tym to inna sytuacja, bo testami HttpClient raczej nie testujesz wszystkich możliwych jsonów. Jeżeli byś tak robił, to wtedy testy parsera nie byłyby potrzebne (bo nie dodawałby nic nowego), ale raczej nie byłby to ładny kod.

Nie po to piszę testy żeby wyłapywać bugi przeglądaniem kodu.

Testy nie są jedyną metodą wyłapywania błędów. Poza tym, co będzie, jak ktoś wywali te Twoje testy? Jak to wyłapiesz, jak nie przeglądem kodu (lub automatycznymi metrykami)?

1
Afish napisał(a):

Ja bym nie testował, można ten czas spędzić lepiej.

W istocie to jest dobra odpowiedź.

Inżyniera oprogramowania polega też na racjonalnym wykorzystaniu środków.
Jeśli bedziemy testować trywialne rzeczy, to może nie starczyć czasu na ważne.

Dodatkowo. Testowanie wcale nie jest najlepszą formą sprawdzania poprawności programu.
Jest dość ekonomiczną - daje niezłą pewnośc przy małym koszcie. Ale to już ISTOTNE uproszczenie.

Typowe testowanie przypomina trochę dowodzenie na zasadzie - twierdzenie pitagorasa działa dla trójkąta 3,4,5 oraz dla 1,1,sqrt(2) więc musi się zgadzać :-)
Decydując się na Test poddajesz się i wykonujesz (rażące) uproszczenie. Ale takie uproszczenie zwykle wystarcza, bo o ile nie piszemy złośliwego kodu
to zwykle sprawdzenie jednego, dwóch przypadków i przypadków brzegowych wystarcza.

Nieco dokładniej można testować w oparciu o właściwości - czego prawie nikt w javie nie robi. (property based tests)
Można też formalnie dowodzić poprawności fragmentów programu, czego z kolei, jeśli od programu nie zalezy czyjeś życie to raczej się nie robi (za duży koszt, za rzadko coś faktycznie wychodzi).

Jesli mamy dość generyczny kod - jak u Ciebie - to szansa zepsucia jest tak mała, że w zasadzie można pominąć.
IMO wystarczy, że gdzieś jakiś inny test tą funkcję wykorzystuje i już możesz przyjąć, że działa.

Jeśli faktycznie komuś uda się zepsuć, a błąd wyjdzie w teście innej funkcji to i tak szukanie źródła problemu będzie proste.

Przy okazji - badając przykład znalazłem jak to spierniczyć:

 public T get() {
        return new LibSupplierAdapter(supplier).get(); // No kto tu przyjdzie i to zmieni? - ja :-)
    }

Spierniczneie polega na tym, że znalazłem nietrywialną kompilującą się wersję, która nie jest poprawna.
(btw tak się ubija Haskella zwykle - tak zwana dupa: _|_ ).
Program w javie spierniczyć jezt zwykle dużo trywialniej - return null; , throw new RuntimeException() . Skompiluje się, ale nie zadziała.
Z tym, że takie implementacje zwykle na tyle łatwo wyłapiesz... że naprawdę nie ma co się silić.

0
jarekr000000 napisał(a):
Afish napisał(a):

Ja bym nie testował, można ten czas spędzić lepiej.

W istocie to jest dobra odpowiedź.

but.why.jpg

Cały czas pytam o powód, a dostaję tylko opinie.

1
TomRiddle napisał(a):

Cały czas pytam o powód, a dostaję tylko opinie.

Podałem Ci już powód — jeżeli jest to przetestowane gdzie indziej, to ten test nic nie dodaje. Ma też sporo wad — jest to kod do napisania, do przejrzenia, do uruchomienia, do utrzymania.

Bycie programistą, to nie (tylko) klepanie kodu, ale dostarczanie wartości biznesowej, rozwiązywanie problemów (wyzwań!) klientów i generowanie kasy. Można testować takie rzeczy, jeżeli klient za to zapłaci, to nawet lepiej, ale ten czas można spędzić na poprawie ważniejszych elementów, uzupełnieniu dokumentacji, wyliczeniu metryk użycia produktu lub zaplanowaniu kolejnej funkcji systemu. Jeżeli adaptery są testowane gdzieś indziej, to dodanie kolejnego testu nie niesie moim zdaniem wystarczającej wartości biznesowej, aby poświęcać na ten proces 25 minut (lub więcej).

1

@TomRiddle weź pod uwagę kontekst to bardzo ważna rzecz, o której nie pisze się w książkach. Książka jak wykłada Ci zasady pisania kodu to nie podaje kontekstu, przez co wychodzisz z przeświadczeniem, że dana zasada praktycznie sprawdzi się zawsze i wszędzie. Efekt końcowy jest taki, że w pracy albo spotykam syf w stylu kopiuj-wklej albo przeinżynierowane potworki.

Gdy kodujesz nie powinieneś jechać automatycznie na zasadach, regułkach, schematach i orać tym projekt.

Motyw jest taki byś balansował, byś wybierał ciągle mniejsze zło, byś był świadomy gdy łamiesz zasadę, byś oczywiście za jakiś potrafił odkręcić to co robisz, by adaptowac kod do nowych potrzeb itp

Mi w tym przypadku intuicja podpowiada, że w ogóle robienie 15 adapterów coś śmierdzi, zwłaszcza jeśli to nic nie robi tylko przekazuje kod dalej.

Ale zobacz też, gdybyś nam zdradził, że projekt nad jakim pracujesz ma wpływ na zdrowie ludzi to pewnie dostałbyś odwrotne odpowiedzi, że w pewnych przypadkach warto testować zależności, że nawet warto je eliminować jeśli jest taka opcja.

0

Jednostkowo - nie ma sensu.
Jako część testu integracyjnego, albo e2e, niejako po drodze - owszem jest. Ale to robisz przy okazji i nie specjalnie celujesz w przetestowanie akurat tego.

Zapomniałem napisać dlaczego:
Dlatego, że to strata czasu, tak samo jak testowanie czy x = y działa poprawnie. Jeśli nie ma w tym logiki to nie ma czego testować jednostkowo.
Testy e2e czy integracyjne testują też za to inne rzeczy - to czy wszystko się ładnie ze sobą spina (żeby nie napisać - integruje). To czy delegacja, wrapper itp działa, to nie tylko kwestia argumentów i wywołań, ale czy działa tam gdzie jest używane ze wszystkim.

Tak samo nie ma sensu testować jednostkowo serwisów, które robią tylko łańcuch wywołań. Za wiele z tego korzyści nie ma. Za to test integracyjny w jakiejś formie da Ci o wiele więcej wartości i owszem jest potrzebny. To w jakiej formie czy e2e, czy na poziomie jakiegoś modułu - to zależy od kontekstu czy monolit czy mikroserwis itd itp.

0
jarekr000000 napisał(a):
Afish napisał(a):

Ja bym nie testował, można ten czas spędzić lepiej.

W istocie to jest dobra odpowiedź.

Inżyniera oprogramowania polega też na racjonalnym wykorzystaniu środków.
Jeśli bedziemy testować trywialne rzeczy, to może nie starczyć czasu na ważne.

I tu wg mnie jest zawarta cała esencja.
Nie testuj adapterów. To nie ma sensu. One nie mają w sobie niczego do testowania. Zależą od jakichś zewnętrznych zasobów. To, co powinieneś testować, to klasy używające tych adapterów, np:

public class ClientAdapter
{
    public Client GetClient()
    {
        return api.GetClient();
    }
}


public class MyClass
{
    public string GetClientName()
    {
        return clientAdapter.GetClient().GetName();
    }
}

I teraz powinieneś zamockować tutaj ClientAdapter i testowac metodę GetClientName. Powinieneś zrobić przynajmniej dwa testy. GetClient - nie zwrócił niczego; GetClient zwrócić poprawny wynik.
Potraktuj te adaptery jako źródło danych. Nie testuje się źródła danych jednostkowo.

4

Przede wszystkim powinno nie testować się kodu dla samego testowania kodu, a testować jakieś zachowania. Efekt końcowy, a nie tylko to czy funkcja została wywołana

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