Pytanie o testowanie kontrolerów oraz profesjonalne podejście do konfiguracji i budowy projektu.

0

Cześć, chciałbym przystąpić do refaktoryzacj mojego restu. Przede wszystkim myślę o testach i zacząłem się tak zastanawiać. Otóż póki co mam kilka unit testów napisanych do jakiś serwisów, które tego wymagały (jeśli metoda serwisu była nakładką jedynie na jakiś wyciąg z bazy to olewałem to). I naszły mnie takie refleksje:
Czy nie wystarczyłoby przetestować po prostu wszystkich metod z kontrolerów ? W sensie no wiadomo, zasymulować wywołanie http i posprawdzać statusy i zwrotne jsony ? Porobić wszystkie przypadki dla danej metody i nie powinno to wystarczyć ? Przecież w tym wszystkim znajduje się cała logika z aplikacji i jeśli te testy przejdą to znaczy, że system działa dobrze. Dane do każdej metody generowałbym sobie za pomocą wczytania testowego sqla za pomocą adnotacji @sql i robił rollback po zakończeniu metody.

Druga sprawa dotyczy zrobienia kilku środowisk i odpowiedniej konfiguracji bazy danych. Bo widzę, że w profesjonalnym podejściu robi się dwa środowiska: programistyczne i produkcyjne. Ja póki co mam po prostu jedno środowisko z jedną bazą danych MySQL na zewnętrznym serwerze, której schemat generuje sobie Hibernate ( korzystam ze spring boota). Jak to się powinno robić z tymi dwoma środowiskami ? Czy to nie powinno być tak, że w Mavenie robię sobie dwa profile i w środowisku produkcyjnym stawiam tego MySQL i schemat generuje za pomocą plików SQL za pomocą jakiegoś narzędzia typu FlyWay ? A na środowisku devovym mam jakąś bazę w pamięci typu H2 lub HSQL i ich schemat generuje już sobie hibernate ? Czy wtedy taką bazę testową powinienem czyścić po każdym nowym wygenerowaniu paczki i wczytywać początkowe dane z plików sql czy jak ? Po prostu nigdzie do tej pory nie znalazłem jakiś materiałów o samej konfiguracji projektu już w takim profesjonalnym podejściu i jak to wszystko powinno wyglądać.

Trzecie ostatnie najkrótsze pytanie: w jaki sposób spring boot mi skonfiguruje bazę danych jeśli sam nie dodam żadnych propertiesów do pliku ani nie utworzę żadnych customowych beanów typu DataSource ?

1

Jeżeli robisz aplikacje restowe to bardzo dobrym podejściem jest tworzenie testów endpointów i sprawdzanie później responsów. Takie testy sprawdzają najważniejsze funkcjonalności systemu i jeżeli są dobrze napisane ułatwiają wdrożenie nowej osoby w projekt.

1

Sprawdzanie publicznego API danego komponentu jest bardzo pożytecznym testem.
Jeżeli Twoja aplikacja nie posiada algorytmów wewnątrz komponentów, które warto przetestować unitowo, to testy publicznych endpointów - symulujących biznesową ścieżkę - jest ok.

Dla mnie w ogólności do code review by wystarczyło. Polecam prezentację, a zwłaszcza końcówkę dot. testów pana Nabrdalika:

1

Trzecie pytanie: Spring Boot to nic więcej jak zbiór auto-konfiguracji do Springa (można tworzyć własne) co do tego co znalazł w classpath (obecność klasy w projekcie) lub property w konfiguracji: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure

Pewne konfiguracje uruchamiają się tylko, gdy zostanie dodana odpowiednia zależność np. sterownik do bazy.

Trzeba zapoznać się z adnotacjami @ConditionalOn* chyba najprościej poczytać kod Spring Boot.

Drugie pytanie: Nie lubię baz embedded. Mogą wskazywać błędy i zachowywać się inaczej niż baza produkcyjna (to samo dotyczy symulowania bazy embedded za pomocą prawdziwej bazy). Można skonfigurować środowisko CI/CD/DEV tak, aby pracowała z taką samą bazą jak produkcyjna na serwerze X (lub maszynie programisty). Nie zawsze trzeba. Poza tym start bazy embedded jest często wolny i mocno spowolnia uruchamianie testów integracyjnych. Szybciej na pustej bazie wgrać DDL na już uruchomionym serwerze. Ważne, aby każdy test miał identyczny stan początkowy. Fajniej mieć oddzielne bazy dla konkretnych testów niż robić rollbacki (moja opinia). Od razu usunąć bazę po zakończeniu testu.

Pierwsze pytanie: Dla bazy relacyjnej w Springu ja chcę testować integracyjnie tak:

  1. przed każdym testem łączę się do prawdziwego serwera PostgreSQL (bardzo szybkie uruchomienie testu integracyjnego REST API)
  2. przed każdym testem chce założyć nową bazę danych (każdy test wykonywany jest na oddzielnej bazie), aby była pełna izolacja i możliwość równoległego wykonywana testów
  3. przed uruchomieniem testu integracyjnego wgrywam schemat SQL aktualnej bazy i opcjonalne pożądane dane
  4. po zakończeniu testu baza jest niszczona

Obecnie mam uproszczony wariant czyli zawsze łącze się do tej samej bazy testowej. Co się nie nadaje do równoległego wykonywania testów integracyjnych. Ale nie widzę problemu, aby testy tworzyły sobie bazę przed wgraniem schematu.

Ogólnie problem można rozwiązać na N sposobów.

Zaletą bez embedded jest możliwość pracy bez żadnego serwera (ale ma to jak dla mnie więcej wad).

0

Dzięki. A mam jeszcze dwa pytania:

  1. Jaka jest różnica w użyciu między @SpringBootTest i @AutoConfigureMockMvc, a @WebMvcTest ?
  2. W jaki sposób testować te kontrolery jeśli wszystkie endpointy wymagają tokena autoryzacji ? W sensie jest np jakiś endpoint, którego zwrotka zależy od tokenu, chociażby "/me" -> jego odpowiedź zależy od tokena autoryzacji przecież. Jak to zasymulować ? Mam przed każdym testem kontrolera zasymulować logowanie, zapisać sobie token, który dostane i dopisywać go do metod ?

EDIT: Ok, jak zasymulowałem w @Before logowanie i zapisałem sobie token to ok, działa. A co jeśli chciałbym sobie wyłączyć zabezpieczenia do niektórych metod na czas testów ? Bo tak jak mówie, u mnie wszystkie metody api są zabezpieczone, ale tylko niektóre wyciągają jakieś info z tokena.

0

@margor90:
Dla przykładu mam 2 endpointy:

  1. „/teams”
    Dostęp jest zabezpieczony i wymaga autoryzacji. Powinienem dostac JSONa z lista wszystkich druzyn. Chchialbym wiec wczytac sqla, ktory utworzy kilka teamow na pusta baze i sprawdzic czy dostalem co chcialem.
    Tutaj chcialbym po prostu wylaczyc zabezpieczenia.

  2. „/teams/invite/{id}”
    Tutaj chcialbym dodac usera o id z URLa do mojej druzyny, ktora zostanie wycianieta z db za pomoca mojego emaila odczytanego z tokena. Chcialbym wiec w sqlu dodac usera(mnie), drugiego usera i ten team. No tylko ze, tutaj jest wymagana autentykacja i jakby musze podsc tam moj token, wiec musze go znac.

0

Można używać różnych konfiguracji w zależności od użytego profilu @Profile (należy uruchamiać test z danym profilem). Czyli na profilu produkcyjnym używasz innej konfiguracji @Configuration, a na testowej lekko zmodyfikowanej lub wcale. Uważam, że lepiej mieć alternatywną konfigurację na testy podobną do produkcyjnej. Wtedy wynik testu jest bliższy rzeczywistemu działaniu systemu niż ją po prostu wyłączać.

0

@margor90:
Ok, mam skonfigurowane te ustawienia na różne profile, ale tak jak mówię. Nadal nie wiem jak rozwiązać sprawę z tokenem autoryzacyjnym ? Bo na potrzeby testów mogę wyłączyć spring security, ale co jeśli używam token, z którego pobieram email użytkownika wysyłającego zapytanie ?

0

Podbijam pytanie również do innych. Chciałbym wytestować np. taką metodę z API:

@GetMapping("/me")
public User me(HttpServletRequest request) {
    String email = TokenAuthenticationService.getUserEmailFromHeader(request);
    return userService.getByEmail(email);
}

Przed każdym testem mam czystą bazę danych. Czy naprawdę jedynym sposobem jest wczytanie skryptu za pomocą @sql dodającego jakiegoś usera, następnie w @Before zalogowanie się i uzyskanie tokenu i dopiero testowanie tutaj z dodanym tokenem do headerów ?

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