Serializacja listy encji

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:

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:

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.

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

0

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

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

Zamiast TestEntity Zwracała jsona?

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:

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:

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.

0

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

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:

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

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

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

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ą.
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ć.
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ś.

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:

@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:

Method threw 'com.fasterxml.jackson.databind.JsonMappingException' exception.
Infinite recursion (StackOverflowError)
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.

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

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 ;)

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

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 :)

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