Spring REST testy integracyjne z TestRestTemplate + "mockowanie" użytkownika

0

Cześć, mam taki problem: mam (prawie?) restowe API które jest oparte o cookies authentication i nie wiem jak ogarnąć testy integracyjne.
Założmy że mam endpoint który jest zabezpiecoznyw ten sposób że nazwa uzytkownika musi się zgadzać z path variable. np.:

   @GetMapping("/accounts/{username}")
    @PreAuthorize("authentication.name == #username")
    fun getAccountDetails(@PathVariable("username") username: String) : ResponseEntity<*> {
        return getAccountDetailsUseCase.getAccountDetails(Username(username))
                .map { ResponseEntity(it, HttpStatus.OK) }
                .getOrElse { ResponseEntity(HttpStatus.NOT_FOUND) }
    }
  1. Jak najlepiej "zmokowac" użytkownika żebym mi wywołanie endpointu przez TestRestTemplate nie zracało 403? Z frontu to działa, więc wiem że mam problem z testami...

  2. Jakie lepsze metody uwierzytelniania dla RESTa zastosować? Basic Auth mi się średnio podoba (trzymanie hasła po stronie przeglądarki...), a do OAUTH2 potrzebowałbym dodatkowego serwera uwierzytelniającego

Wołam @Shalom

1

Nie rozumiem pytania w ogóle i nie wiem co ty chcesz mockować. Test wygląda tak:

  • tworzysz usera w systemie
  • wysyłasz request uwzględniając parametry do auth (czyli cookie, token, header czy co tam potrzebujesz)
  • profit!
0

A jak nie chcesz setupować usera z palca, tylko wystubbować samo security, to masz @WithMockUser: https://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/test-method.html

Oczywiście sam request trzeba zrobić tak samo jak „na produkcji”.

0

@Charles_Ray: no akurat w przypadku TRT WithMockUser nie dziala. Jeśli zać mam zwykłe MockMvc zbudowanym na bazie contextu Springa to działa:

    private lateinit var mockMvc: MockMvc

    @Autowired
    private lateinit var applicationContext: WebApplicationContext

    fun setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build()
    }
0
scibi92 napisał(a):
  1. Jakie lepsze metody uwierzytelniania dla RESTa zastosować? Basic Auth mi się średnio podoba (trzymanie hasła po stronie przeglądarki...), a do OAUTH2 potrzebowałbym dodatkowego serwera uwierzytelniającego

co do OAUTH2 to np Facebook czy Twitter mają chyba swoje serwery uwierzytelniające (oprócz serwerów resourcowych).

Fajnym rozwiązaniem jest też Keycloak

3

@scibi92 klasyka, dzielnie walczysz z problemami które nie występują gdybyś zamiast magicznych mocków używał normalnego http clienta. Serio. Zaoraj te wszystkie mockmvc i testresttemplate i inne gówna. Napisz normalnego klienta do swojego serwisu i stukaj z niego w testach. Wszystkie problemy jak renkom odjoł

Sam piszesz

Z frontu to działa, więc wiem że mam problem z testami...

Więc czemu w testach nie robisz tak samo jak robi front, czyli nie wysyłasz normalnych requestów jak człowiek? ( ͡° ͜ʖ ͡°)

0
Shalom napisał(a):

Więc czemu w testach nie robisz tak samo jak robi front, czyli nie wysyłasz normalnych requestów jak człowiek? ( ͡° ͜ʖ ͡°)

No dobra, a czy to będzie w ramach rollbackowanej transakcji? Nie chcę żeby testy mi narobiły jakieś dane w bazie

1
Pinek napisał(a):
Shalom napisał(a):

Więc czemu w testach nie robisz tak samo jak robi front, czyli nie wysyłasz normalnych requestów jak człowiek? ( ͡° ͜ʖ ͡°)

No dobra, a czy to będzie w ramach rollbackowanej transakcji? Nie chcę żeby testy mi narobiły jakieś dane w bazie

To potrzebujesz osobną bazę do testów. Jak rollbackujesz transakcje to tez nie testujesz w pełni

1

A to ty te testy odpalasz na produkcyjnej bazie czy co? o_O Ustaw testowe properties na jakieś h2/hsql i zrób sobie jakieś @After albo @Before które usuwa wszystko z bazy co test.
Ja osobiście bym chciał żeby mi testy coś pisały do bazy, bo dzięki temu mogę sprawdzić czy coś w ogóle działa...

0
KamilAdam napisał(a):

To potrzebujesz osobną bazę do testów. Jak rollbackujesz transakcje to tez nie testujesz w pełni

To prawda. Ale jak dam osobną bazę dla testów, to może się okazać, że jej schemat nie jest taki sam jak schemat bazy produkcyjnej :( chyba nie ma złotego rozwiązania

Shalom napisał(a):

A to ty te testy odpalasz na produkcyjnej bazie czy co? o_O Ustaw testowe properties na jakieś h2/hsql

Tak, odpalam na bazie produkcyjnej. Jest to baza duża i skomplikowana (jak pewnie większość baz w korpo), i są różnice pomiędzy Oraclem i H2. No niestety...

0
Pinek napisał(a):
KamilAdam napisał(a):

To potrzebujesz osobną bazę do testów. Jak rollbackujesz transakcje to tez nie testujesz w pełni

To prawda. Ale jak dam osobną bazę dla testów, to może się okazać, że jej schemat nie jest taki sam jak schemat bazy produkcyjnej :( chyba nie ma złotego rozwiązania

Są narzędzia do porównywania schematów bazy. Zresztą zawsze można wyeksportować schemat z produkcji i na jego podstawie utworzyć bazę testową. Ale to tylko łaty, bo prawilnym rozwiązaniem jest stawianie bazy ze skryptów. Bo co się stanie jak będziesz potrzebować utworzyć nową produkcję?

3

Tak, odpalam na bazie produkcyjnej.

Szacun za stalowe nerwy. Kiedyś przypadkiem odpalisz jakieś rypnięte query i będzie ciekawie :D

to może się okazać, że jej schemat nie jest taki sam jak schemat bazy produkcyjnej

Od tego masz jeszcze zwykle jakieś testy e2e na środowisku testowym które powinno być mirrorem produkcji.

Jest to baza duża i skomplikowana (jak pewnie większość baz w korpo), i są różnice pomiędzy Oraclem i H2. No niestety...

Proszę cię :P Pracuje ostatnio z Sybase i jeszcze muszę wołać jakieś procedury składowane. Wymaga to trochę gimnastyki ale da się. No i masz https://www.testcontainers.org/modules/databases/oraclexe/ jeśli bardzo chcesz.

0

@Shalom @KamilAdam Muszę coś wyjaśnić, bo źle się wyraziłem :) Nie odpalam testów na bazie produkcyjnej - odpalam na bazie developerskiej. Ale chodziło mi o to, że nie stawiam bazy od zera w testach - testuję na bazie developerskiej (na której klikają inni członkowie zespołu i którą używają inni członkowie zespołu) :)

2

@Pinek To przepis na debug testów co 2 dni jak znalazł.

To nie jest droga do produktywności.

0
nie100sowny napisał(a):

@Pinek To przepis na debug testów co 2 dni jak znalazł.

To nie jest droga do produktywności.

Testy robią rollbacka, nic się nie zapisuje wmiędzyczasie w bazie dla innych. Nie ma debuga co 2 dni.

5
Pinek napisał(a):

Testy robią rollbacka, nic się nie zapisuje wmiędzyczasie w bazie dla innych. Nie ma debuga co 2 dni.

I nikt nie dodaje nowych kolumn, tabel. Jeden kolega może spokojnie testować nowy feature gdzie totalnie przeorał schemat bazy, a drugi w spokoju robi testy pod hotfixa produkcyjnego :-)

Widziałem już taką herezję wiele razy. Zaskakuje mnie jak ludzie uparcie twierdzą, że przecież nie ma żadnego problemu, nikt nikomu nie przeszkasza. A potem na następnym stanie d**y proszą, żeby przez dwa dni nic nie ruszać w bazie...

Zwykle naprawienie tego jest dość proste - można zrobić zrzut bazy i już mamy pierwszy jako taki skrypt inicjalizujący.
Kolejne zmiany można już robić jak ludzie (flyway, liquibase).

0

@jarekr000000: używamy Flyway. Jak ktoś chce zrobić większą zmianę na bazie, robi to sobie na innej bazie developerskiej ;)

0

@Shalom @jarekr000000 @scibi92
Już się pogubiłem - czyli jak w testach wstaje osobno baza H2 do testów, i chcemy faktycznie przetestować transakcje, to znaczy, że w testach nie ma by default rollbakowanej transakcji, tak? Bo w Spring Boot testach chyba transakcje są rollbackowane by default...

1

Jeśli mnie pamięć nie myli to jak rollbackujesz transakcje to wielu rzeczy nie przetestujesz bo JPA jest w stanie przyjąć dużo i dopiero przy zatwierdzaniu transakcji rzuca błędami. Czyli transakcje trzeba zatwierdzać a potem czyścić bazę. Przy czym bazę najbezpieczniej czyścieć nie po teście a przed testem

0

Bo w Spring Boot testach chyba transakcje są rollbackowane by default

@Pinek nic się samo nie dzieje, to raz. Dwa, to bez znaczenia, bo nawet jeśli (da sie to zrobić jakimś @Transactional na teście) to rollback wykonałby się PO całym teście, więc realnie jest ci obojętny. Przecież nie chcesz zeby PO teście zostały ci jakieś dane. Możesz też wyłączyć ten domyślny rollback, ale musisz wtedy pamietać zeby w jakimś after/before czyścic bazę!
Niemniej dawno nie używałem JPA więc nie musiałem przejmować się takimi dziwnymi problemami.

0
Shalom napisał(a):

to bez znaczenia, bo nawet jeśli (da sie to zrobić jakimś @Transactional na teście) to rollback wykonałby się PO całym teście, więc realnie jest ci obojętny.

Hmm skoro jest jakaś opakowująca transakcja, która na początku jest ustawiona na zrobeinei rollbacka, to w zależności od propagacji, mogą być różne rezultaty. I tak jak @KamilAdam wspomniał - wtedy nie testuję faktycznych transakcji, bo JPA przyjmie dużo, a dopiero przy commicie by się pojawiły błedy

1

@Pinek jak zawsze, dzielna walka z problemami nie znanymi w innych systemach ;) Niestety ale jesli używasz magicznych narzędzi jak JPA, musisz liczyć się z tym, że trzeba będzie używać magii żeby to sensownie testować, takie życie :) Możesz dać @Rollback(false) albo @Commit na tych klasach testowych (albo może nawet na base wystarczy)

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