Serializacja listy encji

Serializacja listy encji
S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

Czy istnieje możliwość serializacji listy encji do Json aby później można było ją odtworzyć w teście?
Piszę test metody w której mam m.in. pobieranie danych z bazy:

Kopiuj
List<TestEntity> list = testRepository.findAll();

chciałbym to zamockować i podstawić własną listę ładowaną właśnie z json.
W przypadku listy zwykłych obiektów używam ObjectMappera i metody writeValueAsString a dalej w teście z pliku odtwarzam listę i na niej pracuje.
Kiedy chce zrzucić tym sposobem listę encji dostaję błędy:

Kopiuj
Can not parse field to json
com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy [pl.app.xxx.repositories.xxx#1] - no Session 
(through reference chain: pl.app.xxx.repositories.xxx["roomId"]->pl.app.xxx.repositories.xxx$HibernateProxy$jE4aRmEW["description"])

nie radzi sobie z polami które odwołują się do innych tabel.
Czy zna ktoś może jakiś sposób na rozwiązanie tego problemu.

Wiem, że w teście mogę użyć np. liquidbase, załadować kontekst i pobierać dane z bazy
lub stworzyć klasę która taką listę wygeneruje ale jest z tym dużo pracy bo lista jest dosyć spora dlatego szukam szybszego rozwiązania
i takiego który nie wymaga ładowania całego kontekstu.

edytowany 1x, ostatnio: Riddle
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:3561
1

@szary88:

Obawiam się encje wyjęte z deserializcji JSON będą dalekie od zagwarantowania ich identyczności z pierwotnym stanem, w pierwotnej sesji JPA - aby było przydatne do testu


Bo C to najlepszy język, każdy uczeń ci to powie
Black007
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 10 godzin
0

Chwila, bo nie do końca zrozumiałem o co chodzi.
Chcesz, żeby kod:

Kopiuj
List<TestEntity> list = testRepository.findAll();

Zamiast TestEntity Zwracała jsona?


"Nie popełnia błędów tylko ten, kto nic nie robi"
S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

@Black007:
Nie. TestEntity ma zwracać normalnie listę encji.
Ja chcę to metodę zamockować i podstawić jakąś testową swoją listę.
Żeby się za bardzo nie narobić i nie uruchamiać kontekstu w teście kombinuję, że podczas działania aplikacji lokalnie dodam na chwilę w kodzie:

Kopiuj
List<TestEntity> list = testRepository.findAll();
String json = ObjectMapper.writeValueAsString(list)

json zapisze w resources testów i dalej z tego jsona też przy pomocy ObjectMappera będę tworzył List<TestEntity> list
i dalej:

Kopiuj
when(testRepository.findAll()).thenReturn(list);

oczywiście do przeniesienia danych nie musi być wykorzystany json jeśli istnieje jakaś lepsza metoda.
Generalnie chodzi o to że mam GUI w którym jest mi łatwo wyklikać taką listę, chciałbym ją w pewnym momencie działania aplikacji zapisać i odtworzyć w teście a nie klepać wszystko z palca albo ładować kontekst i pobierać z bazy.

K5
  • Rejestracja:około 6 lat
  • Ostatnio:około 6 godzin
  • Postów:1002
0

Chyba nie rozumiem. Nie możesz po prostu zapisać pliku json z danymi testowymi w resourcach i używać to w teście?

S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

@kixe52: dokładnie to chce zrobić tylko chciałbym ten plik w jakiś sposób wygenerować a nie pisać ręcznie.
Opiszę jeszcze raz cały proces.
Mam 2 aplikację backend to spring boot i front to react.
Uruchamiam obie lokalnie, klikam, wykonuje jakieś operację, rekordy zapisują się w bazię i widzę że coś jest nie tak, coś źle się liczy, dlatego chciałbym zapisać aktualny stan aplikacji i odtworzyć go w teście.
Najbardziej mi zależy żeby lista encji z tego zapytania testRepository.findAll(); w teście jednostkowym spring boota była identyczna jak w momencie kiedy znalazłem błąd klikając aplikację lokalnie, wtedy łatwo znaleźć błąd i poprawić.
Problem polega na tym, że nie wiem jak zapisać listę encji do pliku podczas działania programu aby móc ją łatwo odtworzyć na teście.
Jeżeli to jest lista zwykłych obiektów robię tak:

Kopiuj
List<JakisObiekt> list...
String json = ObjectMapper.writeValueAsString(list)

i w Stringu mam jsona który mogę wrzucić do resources testów i tak listę w testach jednostkowych odtworzyć.
ale jeżeli chce w ten sposób stworzyć json na podstawie listy encji czyli

Kopiuj
List<TestEntity> list = testRepository.findAll();
String json = ObjectMapper.writeValueAsString(list)

to dla Encji ten sposób już nie działa.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
3
  1. W teorii to po prostu brakuje Ci adnotacji @Transactional na metodzie, która wywołuje findall i zapis do jsona.
  2. W praktyce jak będziesz czytał dane z json, a nie z bazy, to będzie to inna aplikacja i niekoniecznie będą występować te same błędy.
    JPA jest popieprzone. A JPA + Spring to już totalny dramat. Obiekty będą zachowywały się inaczej w zależności od tego jak są załadowane i co już z nimi robiono w aplikacji.
    Widaj w świecie, gdzie typy nic nie znaczą.

jeden i pół terabajta powinno wystarczyć każdemu
AK
Zostałeś zwolennikiem / ewangelistą @Transactional ?
S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

@jarekr000000:

  1. dodałem @Transactional na tej metodzie ale cały czas leci to samo:
    Method threw 'java.lang.IllegalStateException' exception.
    Can not parse field to json...
  2. Mam testy integracyjne gdzie tworzę bazę, uzupełniam ją danymi i sprawdzam różne scenariusze, więc liczę, że takie przypadki tam się wywalą. Ale chciałem dodać jeszcze szybkie testy jednostkowe żeby nie ładować za każdym razem całego kontekstu jak coś potrzebuje sprawdzić.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1

Nie pokazałeś całego kodu, więc możliwe, że to nie @Transactional, ale jest też duża szansa, że źle go dodałeś.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

Bardzo możliwe, że źle go dodałem bo nie do końca wiem jak @Transactional działa, muszę doczytać.
jedyne co zrobiłem to dodałem adnotację nad metodą i wygląda to teraz tak:

Kopiuj
@Slf4j
@Service
@AllArgsConstructor
public class Test {

private ObjectMapper MAPPER;
private TestRepository testRepository;

@Transactional
public List<TestEntity> getTest() throws JsonProcessingException {
	List<TestEntity> list = testRepository.findAll();
	String json = MAPPER.writeValueAsString(list);
 ...

na metodzie writeValueAsString teraz lecą takie błędy:

Kopiuj
Method threw 'com.fasterxml.jackson.databind.JsonMappingException' exception.
Infinite recursion (StackOverflowError)
RequiredNickname
Kod potrzebny do testów w kodzie produkcyjnyym? Twój mapper to nie stała skąd zatem upperCase?
S8
kod dodany tylko na potrzeby wygenerowania json :)
RequiredNickname
I tak będziesz na nowo dodawał ten kod by wygenerować jsona jak Ci się encja zmieni? Bez sensu. Dla łatwego setupowania można sobie w testach stworzyć np dedykowane buildery do obiektów.
S8
Jak się encja zmieni mogę zmienić też json-a, nic nie muszę generować. Ty buildera nie będziesz musiał w takim przypadku przerabiać? :) Cały czas nie mogę dostrzec tej przewagi klepania klas ręcznie i uzupełniania danymi nad zaciąganiem danych np. z pliku
MA
  • Rejestracja:ponad 5 lat
  • Ostatnio:dzień
  • Postów:5
2

W TestEntity najpewniej masz dwustronną relacje, dlatego objectMapper wpada w nieskończoną pętle przy zapisie. Musisz odpowiednimi adnotacjami przerwać petlę np. JsonIgnore i znajdą się pewnie jeszcze jakieś inne. Można też zmapować do jakiegoś DTO bez tej relacji.

Ale jeśli dobrze rozumiem to co chcesz zrobić, to najlepszym moim zdaniem rozwiązaniem będzie stworzyć sobie klasy lub metody pomocniczne do testowania i ustawiania stanu bazy dla testu. Za pomocą nich tworzyłbyś odpowiednie encje i zapisywał je w bazie na początku każdego testu.

edytowany 3x, ostatnio: marpi
S8
"Ale jeśli dobrze rozumiem to co chcesz zrobić, to najlepszym moim zdaniem rozwiązaniem będzie stworzyć sobie klasy lub metody pomocniczne do testowania i ustawiania stanu bazy dla testu. Za pomocą nich tworzyłbyś odpowiednie encje i zapisywał je w bazie na początku każdego testu." Tak to teraz robię ale to wymaga załadowania kontekstu przez co test wykonuje się bardzo długo. Mam liquidbase który tworzy schemata bazy w dockerze potem uzupełnia tabele itd. Chciałem po prostu to przyspieszyć dla tego konkretnego testu. pokombinuje jeszcze z adnotacjami w ObjectMapperze
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:3561
0

@marpi:

+1

Praktycznie zawsze tak się rekurencja pipcy jak się dżejsonuje wzajemnie powiązane encje JPA

Poprawne rozwiązanie: encje JPA tam gdzie ich miejsce, na dole w perzystencji, a serialziacja ładnych encji biznesowych (w prostszych projektach), a raczej DTO
Kalekie: nas...ć na encje JPA adnotacji "anty jsonowskich" (poprosić o nie serialziowanie niektóych pól, nie pamiętam dokładnego brzmienia), na przykład na referencji z childa do parenta

@szary88:

Już będziesz wiedział. Niestety, ale tak jak @jarekr000000 napisał, to nie środowisko ani jasne w eksploatacji, ani bezpieczne dla początkujących


Bo C to najlepszy język, każdy uczeń ci to powie
S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

@marpi: faktycznie JsonIgnore dany na niektóre pola z relacjami pomógł i udało się zrzucić taką listę do json-a przy pomocy ObjectMappera. Wiadomo, że jest ona daleka od ideału, wybrakowana i sposób ten raczej nie powinien być używany, ale dla testu moich kilku metod które korzystają tylko z pól encji nadrzędnych sposób ten działa idealnie i nie trzeba tych klas klepać ręcznie ani ładować kontekstu.
Dzięki wszystkim za odpowiedzi, temat zamknięty ;)

Zobacz pozostałe 2 komentarze
S8
Jestem leniwy przyznaje się :) setapowanie obiektów ręcznie to strata czasu jeśli będzie sposób na zautomatyzowanie tego
AK
Jak masz tak testować, to lepiej nie testuj wcale.
RequiredNickname
@szary88: testy to też kod który należy utrzymywać a co za tym idzie trzymać się dobrych praktyk. Ty dlubiesz jak Ci się wydaje. I spoko o ile to pet projekt to eksplorowanie zbioru rozwiązań jest ok o tyle jakbym zobaczył takie cuda w PR w pracy to by z automatu poleciał do kosza.
S8
Szukam lepszego rozwiązania. I cały czas nie widzę tej przewagi jaką miało by mi dać ręcznie klepanie klas i uzupełnianie danymi testowymi nad zaciągnięciem danych testowych z pliku
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około 2 miesiące
  • Postów:3561
0

Chore założenie projektowe wpływa na chory pomysł testu. Przerost mockowania mi to mówi. Jedna prowizorka generuje następne.
Każda encja bazodanowa, która nie pochodzi z silnika JPA, jest niewiarygodna per se. Np jakby pomyśleć o tych częściach testu, które mają zfajlować, taka bzdura jak pchanie do backendu za długiego pola, to nie masz na to szans.

W swoim pomyśle zdeserialziwianych (czy podobnie) encji dodatkowo masz problem, że nie są aktualizwane z nowymi wersjami

Gdybyś do frontu pchał nie encje JPA, ale DTO, czyli znacznie mniej uwikłane obiekty, również testy by były znacznie prostsze (co więcej, na styku dwóch, generalnie niewileu warstw, zaczęło by być podobne do unit testu) - masz mętny integracyjny przez cały pion na mocku, wiec testujesz mocka a nie produkt softwarowy


Bo C to najlepszy język, każdy uczeń ci to powie
S8
  • Rejestracja:ponad 2 lata
  • Ostatnio:2 miesiące
  • Postów:40
0

Dalsza dyskusja już naprawdę nie ma sensu. Skąd pomysł że do frontu pcham encję JPA?!?! Skąd wiesz jakie mam założenia projektowe? po tych kilku linijkach testowego kodu?
Moje pytanie dotyczyło tylko serializacji listy encji i wszystko zostało już wyjaśnione :)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.