Wątek przeniesiony 2023-01-22 22:00 z Java przez cerrato.

Aplikacja do zarządzania wydarzeniami w branży IT - prośba o code review

0

Robię sobie taką prostą aplikację do zarządzania wydarzeniami w branży IT. Mam jeszcze w planach dorobić możliwość wysyłania wiadomości między użytkownikami, ale to zostawiam sobie na koniec. W pierwszej kolejności przerobię ją na REST API, bo chcę mieć API w portfolio i próbować wysyłać gdzieś CV. Też słyszałem, że rekruterzy nie patrzą jakoś bardzo na portfolio, więc tym bardziej nie chcę siedzieć wieczność nad jednym projektem. Czy moglibyście zrobić code review tego co zrobiłem do tej pory?

Link do repozytorium https://github.com/Dreilt/basic-spring-mvc-app

2

Jak przerobisz ... to na tamto, tamto na owo ... to wtedy się zgłaszaj.

0
_13th_Dragon napisał(a):

Jak przerobisz ... to na tamto, tamto na owo ... to wtedy się zgłaszaj.

Różnice będą głównie w kontrolerach, a całej reszty nie można teraz ocenić?

5

Tak z grubsza co mi się rzuciło w oczy:

  1. Brak Readme
  2. Po co używasz dwóch różnych baz danych H2 i Mysql, lepiej użyć tylko jednego z nich
  3. Masz dodanego lomboka, ale używasz go tylko w 2 klasach, a możesz używać również w innych przypadkach - jak dla mnie albo używaj go albo wywal z projektu
  4. W application.yaml masz konfigurację, która jest nadpisywana w innych profilach, trzymaj w niej tylko to co jest wspólne dla każdego profilu
  5. Klasa BaseEntity - wtf?? :)
  6. Zbudowałeś projekty przed pushem? jest wskazana nie istniejąca metoda w testach
  7. Wydaje mi się, że zrobienie AppUserRole jako enum byłoby lepszym rozwiązaniem
  8. Jak masz dużo argumentów np w konstruktorze to niech będą w oddzielnych liniach
  9. Wszystkie klasy są publiczne, można porobić je package-private
  10. Masz pakcage exception, a w nim jeden exception, a resztach porozrzucana po projekcie - jak dla mnie dziwne, ale to może tylko ja :)
  11. CustomHandlerExceptionResolver, if-ów można się pozbyć stosując wzorzec strategię, no i czy na pewno chcesz zwracać nulla?
  12. Jak masz Optional to nie zawsze korzystasz z orElseThrow
  13. Jak klasy BasicSpringMvcAppApplicationTests nie używasz to usuń

EDIT:
I na przyszłość, tak jak wyżej napisał @_13th_Dragon nie dawaj do review czegoś co nie jest skończone

0

@Kamil Czubaszek:

  1. Dodam w przyszłości.
  2. Baza H2 jest dla profilu dev, a MySQL dla profilu prod.
  3. Staram się jak najmniej korzystać z Lomboka, bo podobno są z nim duże problemy.
  4. Skończyła mi się licencja na IntelliJ IDEA Ultimate, a wersja Community nie działa dobrze z profilami i przypadkiem nadpisałem.
  5. https://www.sztukakodu.pl/equals-hashcode/
  6. Nie budowałem, ale zbuduję i sprawdzę to.
  7. Zastanawiam się gdzie najlepiej byłoby umieścić ten wyjątek i chyba najlepiej w pakiecie appuser.
  8. Właśnie ten null mi się tam nie podoba, ale też nie bardzo wiem jak się go pozbyć. Doradzisz coś?

Można powiedzieć, że ta aplikacja jest skończona. Wiadomości to pierwsza rzecz jaka mi przychodzi do głowy, którą chciałbym zaimplementować, ale najpierw chcę poprawić ten kod i zrobić go jak najlepszym. REST API to osobny temat i jak je zrobię to pewnie też poproszę o code review.

Chciałbym jeszcze żebyś zerknął do EventService, np. do metody findOrganizerEvent(). Najpierw pobierane jest wydarzenie, a później jest sprawdzenie czy użytkownik jest organizatorem. Pytanie - czy można to zrobić lepiej/ładniej? Jest kilka metod, w których powtarza się ten schemat.

Zrobiłem sobie taką fasadę AuthenticatedUserFacade, która zwraca użytkownika z SecurityContextu. Zastanawiam się czy tam też nie można czegoś poprawić.

0
Dreilt napisał(a):

@Kamil Czubaszek:

  1. Dodam w przyszłości.
  2. Baza H2 jest dla profilu dev, a MySQL dla profilu prod.
  3. Staram się jak najmniej korzystać z Lomboka, bo podobno są z nim duże problemy.
  4. Skończyła mi się licencja na IntelliJ IDEA Ultimate, a wersja Community nie działa dobrze z profilami i przypadkiem nadpisałem.
  5. https://www.sztukakodu.pl/equals-hashcode/
  6. Nie budowałem, ale zbuduję i sprawdzę to.
  7. Zastanawiam się gdzie najlepiej byłoby umieścić ten wyjątek i chyba najlepiej w pakiecie appuser.
  8. Właśnie ten null mi się tam nie podoba, ale też nie bardzo wiem jak się go pozbyć. Doradzisz coś?

Można powiedzieć, że ta aplikacja jest skończona. Wiadomości to pierwsza rzecz jaka mi przychodzi do głowy, którą chciałbym zaimplementować, ale najpierw chcę poprawić ten kod i zrobić go jak najlepszym. REST API to osobny temat i jak je zrobię to pewnie też poproszę o code review.

Chciałbym jeszcze żebyś zerknął do EventService, np. do metody findOrganizerEvent(). Najpierw pobierane jest wydarzenie, a później jest sprawdzenie czy użytkownik jest organizatorem. Pytanie - czy można to zrobić lepiej/ładniej? Jest kilka metod, w których powtarza się ten schemat.

Zrobiłem sobie taką fasadę AuthenticatedUserFacade, która zwraca użytkownika z SecurityContextu. Zastanawiam się czy tam też nie można czegoś poprawić.

​ad 2 - raczej używa się takiej samy bazy dla każdego profilu, żeby uniknąć potencjalnych problemów
ad 3 - mogą wystąpić problemy, ale jak nie planujesz zmieniać wersji javy czy spring boot to problemy nie powinny wystąpić - na 100% nigdy nie wiadomo
ad 4 - kiepska wymówka :) pracuje na Community i nie miałem problemu z profilami
ad 5 - nie natrafiłem jeszcze na coś takiego, stąd moje wtf, ale już rozumiem dlaczego tak zrobione
ad 6 - lepiej
ad 7 - jakiś defaultowy Exception handler, żeby błąd nie umknął

Jeśli chodzi o EventService to robisz po prostu mniejsze prywatne metody jeśli tylko w tej klasie są potrzebne i je używasz gdzie potrzeba.
Jak sprawdzasz czy Optional jest empty to lepiej zrobić orElseThrow

0

0 testów?

pała, siadaj.

PS: A nie, jednak są. To 4+.
PS: A nie, jednak te testy to jest mock-hell. Takze 2- jednak będzie.

String email = currentUser.getName();
Optional<AppUser> userOpt = appUserRepository.findByEmail(email);
if (userOpt.isEmpty()) {
    throw new AppUserNotFoundException(String.format("User with email %s not found", email));
}

return userOpt.get();

możesz skrócić do

return appUserRepository
  .findByEmail(currentUser.getName())
  .orElseThrow(new AppUserNotFoundException(String.format("User with email %s not found", email)));
0

@Kamil Czubaszek:

ad 4. uruchamiasz aplikację komendą?
ad 11. poprawiłem https://github.com/Dreilt/basic-spring-mvc-app/commit/a2a39932e84f6719b1a302e374d7fd99c4dc8c99 teraz jest dobrze, czy jeszcze można coś poprawić?

@Riddle: co to znaczy mock-hell? Że brak testów integracyjnych? Mógłbyś rozwinąć?

1
Dreilt napisał(a):

@Riddle: co to znaczy mock-hell? Że brak testów integracyjnych? Mógłbyś rozwinąć?

To znaczy że Twoje testy praktycznie nic nie testują; że jest to znaczny przerost formy nad treścią; że 90% tego co tam jest to są setupy mocków, a może 10% to jest faktyczne testowanie czegokolwiek.

Np zobacz sobie na ten test:

@Test
void shouldReturnSavedDefaultProfileImage() {
    // given
    when(profileImageRepository.save(any(ProfileImage.class))).thenAnswer(i -> i.getArguments()[0]);
    // when
    ProfileImage returnedCreatedDefaultProfileImage = profileImageService.createDefaultProfileImage();
    // then
    verify(profileImageRepository, times(1)).save(argThat((ProfileImage savedProfileImage) -> {
        Assertions.assertAll("Testing saved default profile image",
                () -> assertNull(savedProfileImage.getId()),
                () -> assertEquals(returnedCreatedDefaultProfileImage.getFileName(), savedProfileImage.getFileName()),
                () -> assertEquals(returnedCreatedDefaultProfileImage.getFileType(), savedProfileImage.getFileType()),
                () -> assertEquals(returnedCreatedDefaultProfileImage.getFileData(), savedProfileImage.getFileData())
        );
        return true;
    }));
}

Jak dla mnie to ten test testuje praktycznie nic, i możnaby go wywalić bez absolutnie żadnej straty moim zdaniem.

Powinieneś oczywiście napisać test, sprawdzający czy da się stworzyć profil z domyślnym obrazkiem, ale na pewno nie w taki sposób. Już pomijam całkowicie, że ten test za dużo wie o implementacji (dużo za dużo), przez co refaktor kodu jest znacznie utrudniony. Potem będziesz chciał coś zmienić, i zobaczysz że Twoja zmiana powoduje fail połowy testów, i będziesz miał wybór - albo fixować testy, albo nie zrobić refaktoru - oba wyjścia są słabe, bo testy są słabe.

0

@Riddle: to lepiej byłoby zrobić testy integracyjne dla serwisów i kontrolerów?

0
Dreilt napisał(a):

@Riddle: to lepiej byłoby zrobić testy integracyjne dla serwisów i kontrolerów?

Powinieneś napisać testy o większym scope'ie, które nie wiedzą o szczegółach implementacyjnych. Powinny być odporne na refaktor oraz duże zmiany w strukturze, a jednocześnie powinny być zdolne do łapania bugów. Powinieneś też nie używać .verify(), bo to jest tylko źródło cierpnia (nie wiem czy można bardziej przywiązać test do implementacji niż .verify() właśnie).

A to czy nazwiesz je sobie integracyjne, jednostkowe czy jakie tam chcesz nie ma znaczenia.

0

@Riddle: no testy integracyjne to te, które łączą się z bazą danych. Mam zrobić osobną bazę do testów i tak testować serwisy i kontrolery? Przerabiałem kiedyś kurs testów jednostkowych i tak były testowane metody, więc tak też zrobiłem w swojej aplikacji. Kiedyś jak pytałem o coś to dostałem feedback, że jest dobrze jak na początkującego, że się je lepiej czyta jak u kogoś tam w pracy. Mógłbym prosić o przykład jak powinien wyglądać dobry test według Ciebie? Możesz na tym co wrzuciłeś wcześniej.

0
Dreilt napisał(a):

@Riddle: no testy integracyjne to te, które łączą się z bazą danych.

Dobry żart.

Nie wiem skąd to wziąłeś, ale porzuć to źródło które Ci sprzedało taki pomysł.

Dreilt napisał(a):

Mam zrobić osobną bazę do testów i tak testować serwisy i kontrolery? Przerabiałem kiedyś kurs testów jednostkowych i tak były testowane metody, więc tak też zrobiłem w swojej aplikacji.

No to zmarnowałeś czas, bo te "kursy" były nic nie warte.

Jeśli czytasz jakikolwiek kurs, i w tym kursie jest idea "testowanie pojedynczej metody", to wyrzuć ten kurs do śmieci. Nie możesz po prostu wyjąć jakiegoś małego kawałka z aplikacji i "przetestować go", to tak nie działa.

Dreilt napisał(a):

Kiedyś jak pytałem o coś to dostałem feedback, że jest dobrze jak na początkującego, że się je lepiej czyta jak u kogoś tam w pracy. Mógłbym prosić o przykład jak powinien wyglądać dobry test według Ciebie? Możesz na tym co wrzuciłeś wcześniej.

To że są lepsze, nie znaczy że dobre.

Nie nauczysz się pisać dobrych testów w parę godzin, to od razu mówię. To wymaga miesięcy jak nie lat nauki. Dobrym miejscem na start, moim zdaniem byłoby to: youtube.com/watch?v=1Z_h55jMe-M. Obejrzyj najlepiej dwa albo trzy razy. Drugim filmikiem byłby ten youtube.com/watch?v=EZ05e7EMOLM - ten jest dużo trudniejszy, więc polecam 3-4 obejrzeć.

0

@Riddle:

Nie możesz po prostu wyjąć jakiegoś małego kawałka z aplikacji i "przetestować go", to tak nie działa.

No ale właśnie taka jest definicja testu jednostkowego. Testuje się pojedynczy element (jednostkę) programu, np. metodę.

Nie nauczysz się pisać dobrych testów w parę godzin, to od razu mówię.

Wiem, ale mimo wszystko jeśli masz czas to prosiłbym o przykład. Najlepiej jakbyś poprawił ten test, który wcześniej wrzuciłeś.

0
Dreilt napisał(a):

Nie możesz po prostu wyjąć jakiegoś małego kawałka z aplikacji i "przetestować go", to tak nie działa.

No ale właśnie taka jest definicja testu jednostkowego. Testuje się pojedynczy element (jednostkę) programu, np. metodę.

Nope. Nie taka jest definicja.

Po pierwsze, nie taka jest definicja, a po drugie nie ma to żadnego sensu. Test który jest tak ciasno związany z metodą ma tight-coupling jak stąd do księżyca i jest tylko i wyłącznie kosztem dla programisty, i nie dostarcza żadnej wartości. Nie możesz potem zrefaktorować tej metody, bo testy failują.

Dreilt napisał(a):

Nie nauczysz się pisać dobrych testów w parę godzin, to od razu mówię.

Wiem, ale mimo wszystko jeśli masz czas to prosiłbym o przykład. Najlepiej jakbyś poprawił ten test, który wcześniej wrzuciłeś.

Nie da się go "poprawić", bo ten test nic sobą wartościowego nie reprezentuje. Ja bym go po prostu wywalił i napisał inny.

Ale zanim to zrobisz - podesłałem Ci linki do dwóch filmików, obejrzyj je w całości i uważnie.

0

@Riddle: wiem, że to nie ma sensu, ale tak się uczyłem i też przeglądając kody innych widziałem to w taki sposób zrobione. Jak powinno to być zrobione żeby miało sens? Ustawić modyfikator private w getterach i setterach, a obiekt budować builderem?

0
Dreilt napisał(a):

Ustawić modyfikator private w getterach i setterach, a obiekt budować builderem?

Nie ma żadnego sensu w prywatnych getterach/setterach o ile zwracają/ustawiają jedno pole.

0

@_13th_Dragon: to jak można to zrobić?

0
Dreilt napisał(a):

@Kamil Czubaszek:

ad 4. uruchamiasz aplikację komendą?
ad 11. poprawiłem https://github.com/Dreilt/basic-spring-mvc-app/commit/a2a39932e84f6719b1a302e374d7fd99c4dc8c99 teraz jest dobrze, czy jeszcze można coś poprawić?

@Riddle: co to znaczy mock-hell? Że brak testów integracyjnych? Mógłbyś rozwinąć?

4 - korzystam po prostu z flagi -Dspring.profiles.active=nazwa_profilu
11 - wygląda lepiej, ale tam masz sporo części wspólnych w metodach, które można byłoby uspójnić

to jest jakiś projekt z zadania czy tak na własne potrzeby?

0

@Kamil Czubaszek: na własne potrzeby, do portfolio - pisałem o tym w pierwszym poście.

0

@Riddle:

Nie możesz po prostu wyjąć jakiegoś małego kawałka z aplikacji i "przetestować go", to tak nie działa.

To są brednie i robienie z testów religii wynikający... no właśnie, ciekawe z czego?

Najważniejszym celem testu jest sprawdzenie czy aplikacja zachowuje się zgodnie z założeniami.

Jeżeli aplikacja/kod przestaje odzwierciedlać specyfikacje i test zaświeca się na czerwono, to jest to jak najbardziej poprawny test, a tworzenie jakichś bredni nt. tego co się powinno testować (klasa/metoda/unit/package/appka) to religia, a nie inżynieria.

3
WeiXiao napisał(a)

Jeżeli aplikacja/kod przestaje odzwierciedlać specyfikacje i test zaświeca się na czerwono, to jest to jak najbardziej poprawny test, a tworzenie jakichś bredni nt. tego co się powinno testować (klasa/metoda/unit/package/appka) to religia, a nie inżynieria.

Czym innym jest test który jest specyfikacją, a czym innym jest mocking-hell który nic nie wnosi i obok specyfikacji nawet nie stał.

A odnośnie samego tekstu a tworzenie jakichś bredni nt. tego co się powinno testować (klasa/metoda/unit/package/appka) to religia, a nie inżynieria.. - to mylisz cel z konsekwencją. Jak ja piszę testy, to moim celem jest napisać je tak żeby: a. znajdowały bugi, b. były utrzymywalne i nie przeszkadzały w pracy. I tak się składa ze testy które wiedzą o klasach metodach i packageach z reguły bardzo przeszkadzają w pracy.

0

@Riddle

I tak się składa ze testy które wiedzą o klasach metodach i packageach z reguły bardzo przeszkadzają w pracy.

Czym dla ciebie jest "test który wie o klasach"?

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