Organizacja testów integracyjnych

0

Witam,

Chce zaczac pisac testy integracyjne w jednym z projektow, ktory jest oparty o Slim 4, PostgreSQL oraz PHPUnit. Do laczenia sie z baza danych uzyty zostal laminas/laminas-db. Do tego uzyty zostal Phinx do migracji SQL. Czy ktos moze opisac jak stworzyl swoj zestaw do testow zapytani SQL? Na razie postawilem PostgreSQL na dockerze. Do tego stworzylem swoja classes, ktora laczy sie z testowa baza danych. Wszyskto ladnie chodzi na CI/CD. Tylko mam kilka rzyczy do ogarniecia. Tj jak SQL migracja. Czy fixtures. Gdzie przechowywac najlepiej fixutes? Czy oplaca sie testy SQL wywolywac wylacznie w SQL transakcji? Chce tez napiac szybkie testy integracyjne.

1

No ogólnie to logika którą testujesz nie powinna wiedzieć czy łączy się z prawdziwą bazą czy testową.

Tj jak SQL migracja

Chodzi Ci o migracje tabel, np "ruckusing migrations" albo migracje laravelowe? No to je najlepiej odpalić raz przed wszystkimi testami i tyle.

Gdzie przechowywac najlepiej fixutes?

Co masz na myśli dokładnie mówiąc "gdzie"? Tam gdzie masz testy, w kodzie źródłowym.

Czy oplaca sie testy SQL wywolywac wylacznie w SQL transakcji?

No musisz jakoś zrollbackować zmiany z poprzednich testów, i transakcje to dobry sposób żeby to zrobić. Więc tak.

0

To co zrobilem do tej pory to stworzylem docker'a ktory instaluje PostgreSQL + nginx. Wszystkie testy zapytan SQL sa wywolywane na tej testowej bazie. Generalnie, stworzylem dwa pliki phpunit.xml i phpunit.slow.xml. Pierwszy wykonuje wylacznie testy jednostkowe. Drugi wykonuje wyszstkie testy jednostkowe oraz integracyjne. Drugi plik jest o wile wolniejszy, bo za kazdym wywolaniem usuwa baze danych oraz tworzy puste tabele i relacje okolo 600 tabel. Mam 1 duzy plik SQL + SQL migracja nowych zmian. Kazdy test integracyjny jest w transakcji. Wiec nie musze pamietac o wykonaniu truncate ta uzytych tabelach. A latwo ominac tabelke lub dwie. Fixtures trzymam w tym samym folderze w ktorej znajduje sie class z zapytaniami SQL.

Tutaj mam kilka pytan optymalizacyjnych. Moze zamiast tworzyc za kazdym uruchomieniem testow jednostokowych nowa baze moge sprawdzac czy baza istnieje i wywolywac tylko SQL migracje. Tylko czytalem, ze to nie jest dobre podejscie. Tylko utworzenie tych 600 tabel troche sie schodzi. Wiec ma to sense. Bo w CI/CD i tak zawsze wszystko bedzie stawiane na nowo. Wiec local powinien byc ok. I generalnie jak zoptymalizowac te zapytania. Druga sprawa myslalem, ze moze warto dodac tylko tabele, ktore uzywam w testach a nie wszystkie?

0

Generalnie, stworzylem dwa pliki phpunit.xml i phpunit.slow.xml. Pierwszy wykonuje wylacznie testy jednostkowe. Drugi wykonuje wyszstkie testy jednostkowe oraz integracyjne.

Musi byc w tym jakas glebsza mysl ale pierwsze slysze zeby dwa razy odpalac testy jednostkowe.

Drugi plik jest o wile wolniejszy, bo za kazdym wywolaniem usuwa baze danych oraz tworzy puste tabele i relacje okolo 600 tabel.

To jest zadanie dla migracji, nie dla testow.

Moze zamiast tworzyc za kazdym uruchomieniem testow jednostokowych ...

Zwykle powodem ponownego uruchamiania projektu sa wprowadzone do niego zmiany, a te z kolei maja to do siebie, ze wcale nie tak rzadko maja destrukcyjny wplyw na zmergowany wczesniej kod, wiec w takim przypadku rezygnacja z kazdorazowego odpalania testow nie wyglada rozsadnie.

Jeszcze a propos testow:

  • jesli jakas czesc kodu odpalana jest w testach integracyjnych a przy tym jest sama w sobie jednorodym procesem (bez zadnych edge case'ow ktorych akurat test integracyjny nie obejmuje) to nie robisz dla tego kodu osobnych testow jednostkowych, bo po co?

  • tak jak wspomnialem wyzej rozsadnym jest robienie testow jednostkowych wylacznie dla mozliwych wystapien edge case'ow, tzn takich sytuacji ktorych zaden z Twoich testow integracyjnych nie pokrywa

  • nie robisz testow jednostkowych dla wszystkich nowych klas ktore rozszerzaja ten sam kod - zwykle sa one formalnie identyczne wiec szoda czasu jesli roznia sie jedynie nazwami "injectowanych" repozytoriow lub manager'ow

    A tak w ogole to brawo za Slim'a, mialbys kiedys ochote pogadac o swoim projekcie na priv'ie, bo tu na forum wlasciwie wylacznie akolici symfony grasuja :D .

0
proximus-prime napisał(a):

Generalnie, stworzylem dwa pliki phpunit.xml i phpunit.slow.xml. Pierwszy wykonuje wylacznie testy jednostkowe. Drugi wykonuje wyszstkie testy jednostkowe oraz integracyjne.

Musi byc w tym jakas glebsza mysl ale pierwsze slysze zeby dwa razy odpalac testy jednostkowe.

Odpalasz albo jeden albo drugi. W obu przypadkach unity, w drugim testy tylko te wolne.

Drugi plik jest o wile wolniejszy, bo za kazdym wywolaniem usuwa baze danych oraz tworzy puste tabele i relacje okolo 600 tabel.

To jest zadanie dla migracji, nie dla testow.

Nie potrzebna tautologia. Tak czy tak to się musi stać przy odpalaniu testów.

0

Nie potrzebna tautologia. Tak czy tak to się musi stać przy odpalaniu testów.

@Riddle - popraw mnie jesli Cie zle zrozumialem ale czy masz na mysli wielokrotne tworzenie 600 tabel przy okazji odpalania testow zamiast jeden raz migracjami?

Zdarzylo Ci sie kiedys pisac test ktory odpalany samodzielnie chodzil jak zyleta, ale odpalajac wszystkie testy na raz przed push'em do repo inne sie wysypywaly? Pewnie tak, i to sprawia ze nie raz jestes zmuszony odpalac kilkukrotnie kompletny zestaw testow w projekcie przy okazji realizacji jednego ficzera.
I co, za kazdym razem testami mielisz tworzenie 600 tabel? To wg Ciebie jest ok?

0
proximus-prime napisał(a):

Nie potrzebna tautologia. Tak czy tak to się musi stać przy odpalaniu testów.

@Riddle - popraw mnie jesli Cie zle zrozumialem ale czy masz na mysli wielokrotne tworzenie 600 tabel przy okazji odpalania testow zamiast jeden raz migracjami?

Idealnie by było, gdyby każdy test tworzył całkowicie cały system od nowa, to byłaby idealna sytuacja (np plik, instancje, mapy, cache, etc.)

Niestety, niektóre systemy są zbyt kosztowne czasowo, żeby je stworzyć - np tworzenie bazy często trwa zbyt długo; więc żeby zapewnić że testy wykonują się szybko, pewna część musi być współdzielona między testami. Nie jest to idealne, bo wymaga dodatkowej pracy, żeby pozbyć się współdzielonego stanu między testami (np robiąc DELETE FROM, albo cofając transakcje), ale za to zyskujemy szybkie testy, co jest kluczowe.

Zdarzylo Ci sie kiedys pisac test ktory odpalany samodzielnie chodzil jak zyleta, ale odpalajac wszystkie testy na raz przed push'em do repo inne sie wysypywaly? Pewnie tak, i to sprawia ze nie raz jestes zmuszony odpalac kilkukrotnie kompletny zestaw testow w projekcie przy okazji realizacji jednego ficzera.
I co, za kazdym razem testami mielisz tworzenie 600 tabel? To wg Ciebie jest ok?

Oczywiscie, to jeden z popularnych błędów które się pojawiają w testach, najczęściej wtedy kiedy testy coś współdzielą między sobą.

0
Riddle napisał(a):

Idealnie by było, gdyby każdy test tworzył całkowicie cały system od nowa, to byłaby idealna sytuacja (np plik, instancje, mapy, cache, etc.)

Pozwole sobie niezgodzic z ta opinia, ale zostawmy "caly system" i skupymy sie na optymalizacji testow przez pryzmat tworzenia 600 tabel w bazie danych przy kazdorazowym ich "odpalaniu" - tego wlasnie dotyczyla moja wypowiedz.
Twierdze ze cos takiego to jest kompletna masakra i to nie trudny do osiagniecia ideal jest tutaj problemem, ale np niechlujnie zrobione testy ktore zostawiaja smietnik w bazie.

W odniesieniu do baz/y (niezaleznie od tego czy jest to wylaznie dev, czy dodatkwo instalujesz test) tabele tworzysz i ewentualnie seed' ujesz migracjami - kropka.
Robisz to jeden raz instalujac projekt.
Reszta zalezy juz tylko i wylacznie od tego czy Twoje testy "sprzataja" po sobie w bazie czy nie.

0
proximus-prime napisał(a):
Riddle napisał(a):

Idealnie by było, gdyby każdy test tworzył całkowicie cały system od nowa, to byłaby idealna sytuacja (np plik, instancje, mapy, cache, etc.)

Pozwole sobie niezgodzic z ta opinia, ale zostawmy "caly system" i skupymy sie na optymalizacji testow przez pryzmat tworzenia 600 tabel w bazie danych przy kazdorazowym ich "odpalaniu" - tego wlasnie dotyczyla moja wypowiedz.
Twierdze ze cos takiego to jest kompletna masakra i to nie trudny do osiagniecia ideal jest tutaj problemem, ale np niechlujnie zrobione testy ktore zostawiaja smietnik w bazie.

W odniesieniu do baz/y (niezaleznie od tego czy jest to wylaznie dev, czy dodatkwo instalujesz test) tabele tworzysz i ewentualnie seed' ujesz migracjami - kropka.
Robisz to jeden raz instalujac projekt.
Reszta zalezy juz tylko i wylacznie od tego czy Twoje testy "sprzataja" po sobie w bazie czy nie.

No, to jest jeden ze sposobów.

Ale innymi sposobami może być np tworzenie drugiej bazy specjalnie do testów, w której możesz odpalać migracje i seedy jak chcesz, możesz też postawić bazę in-memory, możesz odpalać testy na prawdziwej bazie i cofnąć transakcję, możesz również postawić inną bazę, np SQLite (jeśli Twoja aplikacja jest dialect-agnostic).

Nie mówię że któryś z tych sposobów jest lepszy niż inny, po prostu ilustruję że jest ich dużo, do wyboru do koloru, i można podejść do bazy w testach z różnych stron.

0

Jak to jest z Dependency Injection? Ja zamierzam napisac di.test.php i dolaczac plik do testow integracyjnych. W testach jednostkowych wszystko jest mocked, ale w integracyjnych testujemy zachowanie i zastanawiam sie nad prawidlowym podejscie to tematu.

0
poniatowski napisał(a):

Jak to jest z Dependency Injection? Ja zamierzam napisac di.test.php i dolaczac plik do testow integracyjnych. W testach jednostkowych wszystko jest mocked, ale w integracyjnych testujemy zachowanie i zastanawiam sie nad prawidlowym podejscie to tematu.

To ja Ci proponuję, usuń te swoje "jednostkowe" i pisz tylko te które nazwałeś "integracyjne".

A sprawa podstawiania zależności, jak np baza danych to nie problem z DI tylko najpewniej z nietestowalnym kodem.

0

Nie rozumiem^ Nie widzialem zadnego przykladu nigdy. Moim zdaniem dobrze stworzyc di.test.php i nie wysylac np email, sms, czy pobierac prawdziwych platnosci. W di test moge poustawiac, zeby nie wysylal dany serwis wiadomosci, a bramka platnicza byla powiazana z sandbox. Dla mnie ma to sense.

W testach jednostkowych tam, gdzie nie moge to uzywam mocked i tyle, gdzie moge odnosze sie class i tyle.

0
poniatowski napisał(a):

Nie rozumiem^ Nie widzialem zadnego przykladu nigdy. Moim zdaniem dobrze stworzyc di.test.php i nie wysylac np email, sms, czy pobierac prawdziwych platnosci. W di test moge poustawiac, zeby nie wysylal dany serwis wiadomosci, a bramka platnicza byla powiazana z sandbox. Dla mnie ma to sense.

Oczywiście, że dobrze jest nie wysyłać maili, smsów i płatności.

Ale di to nie jest jedyny sposób żeby uniknąć tego w testach. Świat nie dzieli się na "używam DI" albo "wysyłam maile w testach". W odpowiednio zaprojektowanym systemie, możesz tak przetestować kod, zeby nie wysłał maila bez używania DI. To się tak technicznie nazywa "testable code".

W testach jednostkowych tam, gdzie nie moge to uzywam mocked i tyle, gdzie moge odnosze sie class i tyle.

To są testy o bardzo niskiej wartości.

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