Co robić i czego nie robić inicjalizując dane do testów integracyjnych z Elasticsearch
Podczas tworzenia oprogramowania korzystającego z Elasticsearch do wyszukiwania, agregacji danych lub wyszukiwania BM25/wektorowego, warto stworzyć przynajmniej kilka testów integracyjnych. Choć „mockowanie indeksów” może wydawać się kuszące, ponieważ testy mogą działać w ułamku sekundy, to tak naprawdę testujemy nie rzeczywiste interakcje z Elasticsearch, lecz nasze wyobrażenie o tym, jak działa Elasticsearch. Produkcja może brutalnie zweryfikować takie podejście, zwłaszcza po aktualizacji klastra
Aby złagodzić największe wady testów integracyjnych, kluczowe jest inicjalizowanie Elasticsearch danymi w sposób, który może nie jest optymalny w codziennych scenariuszach produkcyjnych, ale działa efektywnie podczas przygotowania środowiska testowego.
Nie twórz kontenera wciąż od nowa
Prawdopodobnie testowanie funkcji zależnych od Elasticsearch zajmie niewiele czasu, np. ułamki sekundy. Dlatego restartowanie Elasticsearch między testami to kiepski pomysł, ponieważ zmarnujesz dodatkowe kilkadziesiąt sekund na samo uruchamianie ES.
Zamiast tego uruchom Elasticsearch przed wszystkimi testami tylko raz, a potem:
- czyść dane po każdym teście,
- inicjalizuj dane przed każdym testem.
Podpowiedź: Jeśli używasz modułu Testcontainers dla Elasticsearch (np. w Javie), upewnij się, że pole jest oznaczone jako @Container static
albo przynajmniej uruchamiane w @BeforeAll
.
cURL to Twój przyjaciel przed testami
Używanie bibliotek klienckich w kodzie produkcyjnym (który testujemy) to dobry wybór. Ale podczas przygotowania środowiska testowego bardziej „hakerskie” podejście może przynieść korzyści, ponieważ potrzeby przed testami i na produkcji nie zawsze są identyczne.
Używanie cURL do zarządzania danymi w Elasticsearch nie wymaga doktoratu, jak pokazał Philipp we wcześniejszym poście.
Dodatkowy plus: cURL jest niezależny od języka programowania, więc testy będą bardziej zrozumiałe dla osób korzystających z różnych technologii.
Z Testcontainers używanie cURL też jest proste. Na przykład, aby usunąć indeks books
, można to zrobić tak:
elasticsearch.execInContainer(
"curl", "https://localhost:9200/books", "-u", "elastic:changeme",
"--cacert", "/usr/share/elasticsearch/config/certs/http_ca.crt",
"-X", "DELETE"
);
W grupie siła
Indeksowanie pojedynczych dokumentów ma sens w wielu przypadkach, ale nie w ładowaniu danych testowych. Zamiast wysyłać 1000 żądań, aby zaindeksować jeden dokument na raz, wyślij jedno żądanie z 1000 dokumentami do endpointa _bulk.
Z Testcontainers wygląda to tak:
elasticsearch.execInContainer(
"curl", "https://localhost:9200/_bulk?refresh=true", "-u", "elastic:changeme",
"--cacert", "/usr/share/elasticsearch/config/certs/http_ca.crt",
"-X", "POST",
"-H", "Content-Type: application/x-ndjson",
"--data-binary", "@/tmp/books.ndjson"
);
Dzięki temu możesz nawet dodawać dokumenty do wielu indeksów w jednym wywołaniu!
Bądź lokalny
Pamięć cache CPU jest znacznie szybsza niż RAM, a dysk zazwyczaj szybszy niż sieć. Jeśli masz dziesięć przypadków testowych, które korzystają z tego samego zestawu danych, nie ma sensu wysyłać tych samych danych dziesięć razy do tego samego kontenera (bo przecież nie tworzysz nowego kontenera na każdy test, prawda?)
Dlatego podczas tworzenia kontenera użyj .withCopyToContainer(...)
, aby skopiować plik do kontenera raz, a potem korzystać z _bulk
, jak w poprzednim kroku. Wygląda to tak:
static ElasticsearchContainer elasticsearch =
new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withCopyToContainer(MountableFile.forHostPath("src/test/resources/books.ndjson"), "/tmp/books.ndjson");
Ma to sens szczególnie w środowiskach (np. CI), gdzie kontener nie jest uruchamiany lokalnie, lecz pochodzi z innej maszyny.
Podsumowanie
Pomysły przedstawione tutaj przypominają, że uniwersalna mantra w IT: DRY (Don’t Repeat Yourself) dotyczy także inicjalizacji danych testowych. Trzymaj dane w formacie ndjson i ładuj je lokalnie, a oszczędzisz sporo czasu podczas wykonywania testów integracyjnych.
Po więcej przykładów zajrzyj do repozytorium Github.