Egy API tesztje akkor ad hasznos visszajelzést az adatelérésről, ha a tényleges SQL és az alkalmazás válaszai is részt vesznek benne. Egy helyettesített repository megmutathatja, hogy a handler jól hívja a függőséget, de az elgépelt oszlopnév vagy egy hiányzó egyediség nem feltétlenül derül ki. Ebben a példában rövid jegyzeteket hozunk létre és olvasunk vissza. A routert folyamaton belül hívjuk, a tároláshoz viszont valódi PostgreSQL-kapcsolatot használunk.

A teljes projekt az examples/articles/postgres-api könyvtárban található. Rögzített közvetlen függőségei: Axum 0.8.9, SQLx 0.8.6, serde 1.0.229, Tokio 1.53.1, Tower 0.5.3, serde_json 1.0.151 és http-body-util 0.1.4. A Cargo.lock a tranzitív verziókat is megőrzi. A példát Rust 1.97.1 és edition 2024 mellett fordítottuk; a verziórögzítés a reprodukálhatóságot szolgálja, későbbi biztonsági frissítések helyett nem elegendő.

A szerződés a migrációban is jelenjen meg

A jegyzet címe kötelező, levágott szélső szóközök után nem lehet üres, és legfeljebb kétszáz Unicode skalárértéket tartalmazhat. A cím egyedi; a másodszor beküldött azonos cím 409 Conflict választ ad. A sikeres létrehozás 201 Created, az olvasás 200 OK, a hiányzó azonosító 404 Not Found. Ezeket az elvárásokat előre rögzítjük, nem a teszt futása után választjuk hozzá a meglévő viselkedéshez.

CREATE TABLE notes (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    title TEXT NOT NULL UNIQUE,
    CHECK (char_length(btrim(title)) BETWEEN 1 AND 200)
);

A PostgreSQL korlátozásokról szóló dokumentációja alapján az egyedi szabály a táblához tartozik. Nem elég előbb lekérdezni, hogy létezik-e a cím, majd külön beszúrni: két párhuzamos kérés mindkét ellenőrzése átmehetne. A példában az adatbázis 23505 hibakódját alakítjuk ütközéssé. Más adatbázishiba általános szerverhibát ad, belső SQL-részletet nem teszünk a kliens válaszába.

Az alkalmazás a címet előbb ellenőrzi, az SQL pedig paraméterkötést használ. A beküldött szöveg így adat marad. A SQLx dokumentációja bemutatja ezt az elválasztást. A futásidejű query_as használata ebben a mintában nem jelent fordításidejű SQL-ellenőrzést; éppen ezért fontos, hogy a lekérdezések a tesztben ténylegesen végrehajtódjanak.

Tesztenként külön adatbázist használj

A #[sqlx::test] makró PgPool paraméterrel külön tesztadatbázist hoz létre, és lefuttatja a megadott migrációkat. Ehhez a kapcsolatot biztosító felhasználónak adatbázis-kezelési jogosultság kell. A SQLx tesztmakró dokumentációja a létrehozás és takarítás részleteit is ismerteti. A szerepet ezért egy eldobható, helyi PostgreSQL-példányban használd; közös fejlesztői vagy éles szerver nem megfelelő célpont.

Egy egyszerű rollback nem mindig helyettesíti ezt az elkülönítést. Ha a teszt és a handler külön kapcsolatot kér a poolból, nem feltétlenül ugyanabban a tranzakcióban dolgoznak. Könnyen úgy tűnhet, hogy a teszt takarít, miközben a handler másik kapcsolaton véglegesít. Itt minden teszt saját adatbázist kap, így az egyik tesztben használt cím nem ütközik a másikéval.

A következő parancsok külön konténert indítanak, és kizárólag a loopback címre teszik elérhetővé. A jelszó ebben a példában eldobható tesztadat; ne használd más rendszerben. A teszt indítása előtt várd meg, amíg a pg_isready sikeres.

docker run --rm -d --name rusthu-api-example \
  -e POSTGRES_USER=rusthu -e POSTGRES_PASSWORD=local-example-only \
  -e POSTGRES_DB=rusthu_examples \
  -p 127.0.0.1:55438:5432 postgres:17-alpine
docker exec rusthu-api-example pg_isready -U rusthu -d rusthu_examples
cd examples/articles/postgres-api
DATABASE_URL=postgres://rusthu:[email protected]:55438/rusthu_examples \
  cargo test --locked
docker stop rusthu-api-example

A teljes kérési útvonalat ellenőrizd

Az Axum routerét a Tower oneshot hívásával érjük el. A teszt valódi HTTP-kérésobjektumot ad át, így a route kiválasztása, a JSON-feldolgozás, a state és a válaszkód együtt vizsgálható. Külső HTTP-listener nem indul: ez a konkrét hálózati kiszolgálást, proxyt vagy TLS-beállítást nem ellenőrzi. A router és handlerek leckéje és az extractorok és state adja ehhez az alapot.

Az első teszt létrehoz egy magyar ékezetes címet, kiolvassa a választ, majd a visszakapott azonosítóval lekéri ugyanazt a jegyzetet. Nem feltételezi, hogy az első azonosító egy: az identity érték az adatbázis feladata. A teszt a tényleges cím és azonosító egyezőségét ellenőrzi, így a rossz visszaolvasási útvonal is hibát okozna.

A további esetek a hiányzó sort, az üres vagy túl hosszú címet, az egyedi cím ütközését és egy SQL-nek látszó szöveget vizsgálnak. A hibás bemenet után közvetlenül megszámoljuk a táblasorokat, tehát nemcsak a válaszkódot nézzük. Ütközésnél pontosan egy sornak kell maradnia. Az SQL-szöveges esetben a visszaolvasott címnek változatlanul egyeznie kell a beküldött adattal.

Mit bizonyít a helyi futás?

A kísérőteszt öt API-esetet fed le valódi migrációval és PostgreSQL-tárolással. Ez jó alap a későbbi módosításokhoz, de nem tartalmaz jogosultságkezelést, párhuzamos terhelésmérést vagy adatbázis-kiesési gyakorlatot. Ezekhez további, célzott tesztek kellenek. Az adatbázist érintő beállításokat is külön kezeld: a példában a lokális tesztkapcsolat egyszerű, távoli szolgáltatáshoz hitelesített TLS-kapcsolatot kell tervezni.

A következő fejlesztésnél először adj új esetet a szerződéshez. Ha például felhasználónként legyen egyedi a cím, az adatmodellnek és a hibatesztnek együtt kell változnia. Ha oldalszámozás kerül az API-ba, a rendezési és határfeltételeket is rögzítsd. Az integrációs teszt akkor marad értékes, ha a felhasználó által érzékelhető viselkedést őrzi, és közben valóban végighalad a tárolási rétegen.

A példa ellenőrzési környezete

Rust 1.97.1 és edition 2024 mellett a teljes projekt lefordult. Az öt API-integrációs teszt egy kizárólag helyben elérhető, eldobható PostgreSQL 17 konténerben sikeresen lefutott, tesztenként külön adatbázissal. A migráció és a tényleges lekérdezések részt vettek a futásban. Külső HTTP-kiszolgálót, TLS-t és éles adatbázist nem vizsgáltunk.

ℹ
Megjegyzés

AI-val segített szerkesztőségi anyag. A technikai ellenőrzés környezetét a cikk végén jelezzük; emberi szakmai lektorálást ez a megjelölés nem állít.