author-avatar
Dominik Martyniak
Java 7 minut

Testcontainers - Spring boot Testowanie Integracyjne

Testcontainers to bardzo użyteczne narzędzie, które znacząco ułatwia proces testowania integracyjnego aplikacji, szczególnie tych opartych na architekturze mikroserwisów, takich jak aplikacje Springowe. Dzięki wykorzystaniu kontenerów Docker, Testcontainers pozwala na tworzenie izolowanych środowisk testowych, które dokładnie imitują produkcję, co z kolei przyczynia się do większej wiarygodności testów. Oto kilka kluczowych aspektów, które wyróżniają Testcontainers w kontekściedobrych praktyk w projektowaniu oprogramowania:

  1. Replikowalność środowiska: Testcontainers zapewniają, że każdy członek zespołu programistycznego oraz środowisko CI/CD może uruchomić testy integracyjne w identycznie skonfigurowanym środowisku, co eliminuje problem "u mnie działa". Dzięki temu narzędziu łatwiej jest diagnozować i rozwiązywać problemy specyficzne dla środowiska.
  2. Szybkość i elastyczność: Uruchamianie zależności takich jak bazy danych, kolejki wiadomości czy inne serwisy zewnętrzne w kontenerach znacząco skraca czas potrzebny na konfigurację środowiska testowego. Testcontainers umożliwia dynamiczne zarządzanie życiem tych kontenerów, co jest szczególnie przydatne w automatyzacji testów.
    Integracja z popularnymi frameworkami: Testcontainers świetnie integruje się z popularnymi frameworkami i bibliotekami w ekosystemie Javy, takimi jak Spring Boot, JUnit, Spock czy Selenium. Dzięki temu programiści mogą łatwo dodać testy integracyjne do swoich projektów bez konieczności głębokich zmian w istniejącym kodzie.
  3. Zwiększenie wiarygodności testów: Używając rzeczywistych instancji serwisów zamiast mocków czy stubów, Testcontainers pozwala na bardziej realistyczne testowanie zachowania aplikacji. To z kolei prowadzi do lepszego zrozumienia interakcji pomiędzy różnymi komponentami i minimalizacji ryzyka niespodzianek po wdrożeniu na produkcję.
  4. Bezpieczeństwo i izolacja: Każdy kontener może być traktowany jako odizolowane środowisko, co zwiększa bezpieczeństwo testów. Testcontainers zarządza cyklem życia kontenerów, automatycznie usuwając je po zakończeniu testów, co zapewnia, że zasoby są czyste i nie zostają niechciane artefakty.
    Doskonałe narzędzie do artykułów i prezentacji: Promowanie używania Testcontainers w artykułach, blogach czy na konferencjach jest doskonałym sposobem na popularyzację nowoczesnych praktyk w testowaniu oprogramowania. Tego rodzaju treści często cieszą się dużym zainteresowaniem wśród programistów, co przekłada się na lepsze pozycjonowanie i widoczność serwisów je publikujących.

 

Zacznijmy od konfiguracji Gradle.



Najpierw musimy dodać odpowiednie zależności do pliku build.gradle:

 

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.testcontainers:postgresql:1.17.3'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'

}

Upewnij się, że wersje zależności są aktualne.

 

Konfiguracja testów.


Teraz musimy skonfigurować nasze testy integracyjne, aby używały Test Containers.  W tym celu stworzymy nasza główną klase testów integracyjnych, która będzie rozszerzana przez poszczególne testy.


import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@SpringBootTest
public class MyIntegrationTest {

    @Container
    public static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:latest")
        .withDatabaseName("mydb")
        .withUsername("myuser")
        .withPassword("mypassword");

    @DynamicPropertySource
    static void postgresProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
        registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
        registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
    }

}

 

W trakcie tego procesu, adnotacja @SpringBootTest pełni kluczową rolę, informując nas, że podczas testów chcemy stworzyć pełen kontekst Springowy. Można to porównać do uruchamiania całej aplikacji. Z kolei adnotacja @Testcontainers wskazuje, że nasza aplikacja będzie korzystać z kontenerów.

Pierwszym z tych kontenerów jest kontener bazodanowy, zawierający najnowszy obraz PostgreSQL oraz podstawowe dane logowania, takie jak nazwa użytkownika, baza danych i hasło.

Nasuwające się pytanie brzmi: jak dynamicznie przekazać takie dane do testów? W tym przypadku przychodzi z pomocą DynamicPropertySource. Nie będziemy teraz wchodzić w szczegóły tego rozwiązania, ale można je potraktować jako materiał na osobny wpis. W skrócie, DynamicPropertySource umożliwia nam dynamiczne ustawianie nowych właściwości (properties), w tym przypadku danych z adnotacji @Container, w miejscu odpowiednim dla URL do bazy danych, nazwy użytkownika i hasła.



Przykład wykorzystania Testcontainers



Za pomocą tak skonfigurowanej klasy możemy teraz stworzyć test, który będzie mógł korzystać z repozytorium oraz wykonywać różnorodne operacje na naszej aplikacji. Na przykład, możemy napisać test, który zapisuje dane do bazy danych i sprawdza, czy operacja zakończyła się sukcesem. Poniżej znajduje się prosty przykład testu:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;


public class MyIntegrationTest extends MyIntegrationTest {

    @Autowired
    private MyRepository myRepository;

    @Test
    void testDatabaseOperation() {
        //given
        MyEntity entity = new MyEntity();
        entity.setName("Przykładowa nazwa");
        
       //when
        myRepository.save(entity);
        
        //then
        MyEntity savedEntity = myRepository.findById(entity.getId()).orElse(null);
        
        assertNotNull(savedEntity);
        assertEquals("Przykładowa nazwa", savedEntity.getName());
    }
}

 

W powyższym przykładzie testujemy operację zapisu do bazy danych, a następnie sprawdzamy, czy dane zostały poprawnie zapisane i mogą być pobrane. Dzięki wykorzystaniu kontenera Test Containers, test ten działa w izolowanym środowisku, co pozwala na pewne i efektywne testowanie operacji na bazie danych w aplikacji Spring.


Podsumowanie

W tym artykule dowiedzieliśmy się, jak skonfigurować testy integracyjne w aplikacji Spring, wykorzystując narzędzie Test Containers i Gradle. Począwszy od adnotacji @SpringBootTest, która pozwala na stworzenie pełnego kontekstu Springowego podczas testów, aż po @Testcontainers, który informuje, że nasza aplikacja będzie korzystać z kontenerów, pokazaliśmy, jak krok po kroku przygotować środowisko do testowania.

Przy użyciu kontenera bazodanowego, na przykład PostgreSQL, omówiliśmy sposób dynamicznego przekazywania danych testowych za pomocą DynamicPropertySource. Dzięki temu możemy stworzyć testy, które wykorzystują repozytoria, operują na bazach danych i sprawdzają poprawność operacji.

Testowanie kontenerów za pomocą Test Containers to skuteczny sposób na zapewnienie, że nasza aplikacja działa poprawnie w rzeczywistym środowisku. Dzięki tej technice możemy być pewni, że nasze testy integracyjne są wiarygodne i odzwierciedlają działanie aplikacji w produkcji."

4
[spring boot, java, testy, test containers]

Więcej od Dominik Martyniak

Więcej artykułów