Async Testler¶
🌐 Translation by AI and humans
This translation was made by AI guided by humans. 🤝
It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖
You can improve this translation by helping us guide the AI LLM better.
Sağlanan TestClient ile FastAPI uygulamalarınızı nasıl test edeceğinizi zaten gördünüz. Şimdiye kadar yalnızca senkron testler yazdık, yani async fonksiyonlar kullanmadan.
Testlerinizde asenkron fonksiyonlar kullanabilmek faydalı olabilir; örneğin veritabanınızı asenkron olarak sorguluyorsanız. Diyelim ki FastAPI uygulamanıza request gönderilmesini test etmek ve ardından async bir veritabanı kütüphanesi kullanırken backend'in doğru veriyi veritabanına başarıyla yazdığını doğrulamak istiyorsunuz.
Bunu nasıl çalıştırabileceğimize bir bakalım.
pytest.mark.anyio¶
Testlerimizde asenkron fonksiyonlar çağırmak istiyorsak, test fonksiyonlarımızın da asenkron olması gerekir. AnyIO bunun için güzel bir plugin sağlar; böylece bazı test fonksiyonlarının asenkron olarak çağrılacağını belirtebiliriz.
HTTPX¶
FastAPI uygulamanız async def yerine normal def fonksiyonları kullanıyor olsa bile, altta yatan yapı hâlâ bir async uygulamadır.
TestClient, standart pytest kullanarak normal def test fonksiyonlarınızın içinden asenkron FastAPI uygulamasını çağırmak için içeride bazı “sihirli” işlemler yapar. Ancak bu sihir, onu asenkron fonksiyonların içinde kullandığımızda artık çalışmaz. Testlerimizi asenkron çalıştırdığımızda, test fonksiyonlarımızın içinde TestClient kullanamayız.
TestClient, HTTPX tabanlıdır ve neyse ki API'yi test etmek için HTTPX'i doğrudan kullanabiliriz.
Örnek¶
Basit bir örnek için, Bigger Applications ve Testing bölümlerinde anlatılana benzer bir dosya yapısı düşünelim:
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
main.py dosyası şöyle olur:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Tomato"}
test_main.py dosyasında main.py için testler yer alır, artık şöyle görünebilir:
import pytest
from httpx import ASGITransport, AsyncClient
from .main import app
@pytest.mark.anyio
async def test_root():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
response = await ac.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Tomato"}
Çalıştırma¶
Testlerinizi her zamanki gibi şu şekilde çalıştırabilirsiniz:
$ pytest
---> 100%
Detaylı Anlatım¶
@pytest.mark.anyio marker'ı, pytest'e bu test fonksiyonunun asenkron olarak çağrılması gerektiğini söyler:
import pytest
from httpx import ASGITransport, AsyncClient
from .main import app
@pytest.mark.anyio
async def test_root():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
response = await ac.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Tomato"}
İpucu
Test fonksiyonu artık TestClient kullanırken eskiden olduğu gibi sadece def değil, async def.
Ardından app ile bir AsyncClient oluşturup await kullanarak ona async request'ler gönderebiliriz.
import pytest
from httpx import ASGITransport, AsyncClient
from .main import app
@pytest.mark.anyio
async def test_root():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
response = await ac.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Tomato"}
Bu, şu kullanıma denktir:
response = client.get('/')
...ki daha önce request'leri TestClient ile bu şekilde gönderiyorduk.
İpucu
Yeni AsyncClient ile async/await kullandığımızı unutmayın; request asenkron çalışır.
Uyarı
Uygulamanız lifespan event'lerine dayanıyorsa, AsyncClient bu event'leri tetiklemez. Tetiklendiklerinden emin olmak için florimondmanca/asgi-lifespan paketindeki LifespanManager'ı kullanın.
Diğer Asenkron Fonksiyon Çağrıları¶
Test fonksiyonu artık asenkron olduğundan, testlerinizde FastAPI uygulamanıza request göndermenin yanında başka async fonksiyonları da (çağırıp await ederek) kodunuzun başka yerlerinde yaptığınız gibi aynı şekilde kullanabilirsiniz.
İpucu
Testlerinize asenkron fonksiyon çağrıları entegre ederken RuntimeError: Task attached to a different loop hatasıyla karşılaşırsanız (ör. MongoDB'nin MotorClient kullanımı), event loop gerektiren nesneleri yalnızca async fonksiyonların içinde oluşturmanız gerektiğini unutmayın; örneğin bir @app.on_event("startup") callback'i içinde.