Profile Spring są metodą kontroli sposobu budowania kontekstu aplikacji. Określają one, które fasole i wartości konfiguracyjne są używane przez kontener wstrzykiwania zależności do konfiguracji aplikacji. Korzystanie z profili umożliwia programistom i administratorom zmianę konfiguracji aplikacji za pomocą jednego przełącznika.
Testy integracyjne
Jednym z obszarów, w których profile mogą być szczególnie przydatne, są testy integracyjne. Testy integracyjne, jak sama nazwa wskazuje, służą do sprawdzania, czy komponenty systemu współpracują ze sobą, tj. współdziałają zgodnie z oczekiwaniami. Jednymi z takich komponentów są bazy danych, serwery pocztowe, systemy rozliczeniowe czy ogólnie usługi zewnętrzne. Czasami nie chcemy uruchamiać naszych testów przeciwko tym systemom, aby uniknąć niepotrzebnych lub nawet szkodliwych efektów ubocznych naszych testów. Innym częstym powodem jest przyspieszenie kompilacji na maszynach deweloperów, ale dokładne uruchomienie testów w środowiskach CI/CD. Poza tym może to również pomóc w uczynieniu ich deterministycznymi. Wygodnym narzędziem do tego są profile Spring.
Dlaczego testy integracyjne wymagają określonego profilu?
W jednym z naszych projektów chcieliśmy skonfigurować kontekst wszystkich testów integracyjnych nieco inaczej niż w aplikacji produkcyjnej, tj. musieliśmy zmienić adres url bazy danych używanej do tych testów i, co ważniejsze, chcieliśmy wyczyścić dane za pomocą liquibase. Chodzi o ustawienie następujących właściwości:
spring.datasource.url=jdbc:postgresql://localhost:5432/db-integration-tests
spring.liquibase.drop-first=true
Oczywistym rozwiązaniem było użycie profili Spring i niestandardowego pliku właściwości, ponieważ Spring czyta m.in, application-{profile}.properties
plik, w którym profile
jest nazwą aktualnie aktywnego profilu. Następnie Spring zawsze łączy go z application.properties
(pierwszy zastępuje drugi). Zdecydowaliśmy więc, że wszystkie testy Spring będą uruchamiane z test
profil.
Ustawienie test
profil
Istnieje kilka sposobów na skonfigurowanie aktywnych profili w testach opartych na Spring:
- Konfigurowanie IDE do aktywacji
test
profil - działa i jest elastyczny, ale wymaga ręcznej konfiguracji na komputerze każdego dewelopera - @TestPropertySource - działa, ale wymaga oznaczenia wszystkich testów tą adnotacją lub dziedziczenia po nadklasie posiadającej tę adnotację. W naszym przypadku nie chcieliśmy wprowadzać takiego dziedziczenia i ograniczać się.
- @ActiveProfiles - wymaga również oznaczenia wszystkich testów tą adnotacją lub dziedziczenia z superklasy posiadającej tę adnotację. Poza tym ma jeszcze jedną poważną wadę: jest ostateczna, tj. profile Spring przekazywane przez zmienne środowiskowe są ignorowane, ponieważ jest to priorytet. Jeśli chcesz aktywować inny profil za pomocą wiersza poleceń lub IDE, nie masz już opcji... prawie. Jest jeden sposób: możesz napisać własny ActiveProfilesResolver która pobierze zmienne środowiskowe przed wartościami wprowadzonymi w
@ActiveProfiles
adnotacja. Możesz nawet zobaczyć działające rozwiązanie tutaj. Zapewnia najlepszą elastyczność, ale wiąże się z kosztami utrzymania dodatkowego kodu. application.properties
w testowej bazie kodu - możemy po prostu umieścićspring.profile.active=test
w tym pliku, a wszystkie testy integracyjne będą odbierać ten profil, ale także będą respektować nadpisanie aktywnych profili z wiersza poleceń. Brzmi dobrze, prawda? Nie do końca. Problem polega na tym, że to zacieniaapplication.properties
z głównej bazy kodu (która zwykle przechowuje domyślne wartości konfiguracyjne dla aplikacji), więc aby było to użyteczne, musiałbyś skopiować i utrzymywać oba pliki w synchronizacji, z wyłączeniem ustawień specyficznych dla testu, co czyni to jeszcze trudniejszym.- profil domyślny - gdy Spring uruchamia kontekst aplikacji bez jawnie ustawionych profili, zgłasza interesującą rzecz:
Spada z powrotem do default
profil. Jest to nieco mylące, ponieważ kiedy bierzemy Środowisko fasola i zadzwoń getActiveProfiles()
otrzymujemy pustą tablicę, ale default
profil jest jednak aktywny i można używać application-default.properties
! Możemy więc użyć go jako dodatkowego application.properties
i umieścić tutaj ustawienia specyficzne dla testów. W przypadku plików właściwości prawdopodobnie odpowiadałoby to naszym potrzebom, ale gdy używamy alternatywnych beanów do testów, musimy oznaczyć fałszywe implementacje za pomocą @Profile("test")
. Wyglądałoby to źle i wprowadzałoby w błąd @Profile("default")
. Ponadto, w mojej osobistej opinii application-test.properties
jest pierwszym wyborem do sprawdzania wartości konfiguracyjnych dla testów. Dlatego zdecydowaliśmy się aktywować test
profil w application-default.properties
:
spring.profiles.active=test
Teraz możemy użyć application-test.properties
jako domyślna konfiguracja dla testów integracyjnych, ale jeśli zdecydujesz się zmienić profil lub dodać jeszcze jeden, możesz to zrobić.
Wnioski
Jak widać przy użyciu default
umożliwia zmianę domyślnego profilu dla testów integracyjnych z następującymi korzyściami:
- nie ma potrzeby wprowadzania dodatkowego dziedziczenia
- możliwość zastępowania aktywnych profili za pomocą profili środowiskowych
- brak dodatkowego kodu do utrzymania
Chcesz porozmawiać z naszymi ekspertami o niestandardowych rozwiązaniach w zakresie tworzenia oprogramowania dla Twojej firmy?
P: Czym są profile Spring?
O: Profile Spring są metodą kontrolowania sposobu budowania kontekstu aplikacji. Określają one, które ziarna i wartości konfiguracyjne są używane przez kontener wstrzykiwania zależności do skonfigurowania aplikacji. Korzystanie z profili umożliwia programistom i administratorom zmianę konfiguracji aplikacji za pomocą jednego przełącznika.
P: Czym są testy integracyjne i dlaczego wymagają określonych profili?
O: Testy integracyjne służą do sprawdzania, czy komponenty systemu współpracują ze sobą, takie jak bazy danych, serwery pocztowe, systemy rozliczeniowe lub usługi zewnętrzne. Czasami nie chcemy uruchamiać naszych testów przeciwko tym systemom, aby uniknąć niepotrzebnych lub nawet szkodliwych efektów ubocznych naszych testów. Profile Spring są wygodnym narzędziem do konfigurowania kontekstu testów integracyjnych inaczej niż w przypadku aplikacji produkcyjnej, np. zmieniając adres URL bazy danych używanej do testów lub czyszcząc dane.