विषय पर बढ़ें

Async Tests

🌐 एआई और मनुष्यों द्वारा किया गया अनुवाद

यह अनुवाद मनुष्यों के मार्गदर्शन में एआई द्वारा किया गया है। 🤝

इसमें मूल अर्थ को गलत समझने या अप्राकृतिक लगने आदि जैसी गलतियाँ हो सकती हैं। 🤖

आप हमें एआई LLM को बेहतर मार्गदर्शन करने में मदद करके इस अनुवाद को बेहतर बना सकते हैं।

अंग्रेज़ी संस्करण

आपने पहले ही देखा है कि दिए गए TestClient का उपयोग करके अपनी FastAPI applications को कैसे test किया जाता है। अब तक, आपने केवल synchronous tests लिखना देखा है, async functions का उपयोग किए बिना।

अपने tests में asynchronous functions का उपयोग कर पाना उपयोगी हो सकता है, उदाहरण के लिए, जब आप अपने database को asynchronously query कर रहे हों। कल्पना करें कि आप अपनी FastAPI application को requests भेजना test करना चाहते हैं और फिर verify करना चाहते हैं कि आपके backend ने async database library का उपयोग करते हुए database में सही data सफलतापूर्वक लिखा है।

आइए देखें कि हम इसे कैसे काम करवा सकते हैं।

pytest.mark.anyio

अगर हम अपने tests में asynchronous functions call करना चाहते हैं, तो हमारे test functions asynchronous होने चाहिए। AnyIO इसके लिए एक अच्छा plugin प्रदान करता है, जो हमें specify करने देता है कि कुछ test functions को asynchronously call किया जाना है।

HTTPX

भले ही आपकी FastAPI application async def के बजाय सामान्य def functions का उपयोग करती हो, यह अंदर से फिर भी एक async application होती है।

TestClient अंदर कुछ magic करता है ताकि standard pytest का उपयोग करते हुए आपकी सामान्य def test functions में asynchronous FastAPI application को call किया जा सके। लेकिन जब हम इसे asynchronous functions के अंदर उपयोग करते हैं, तो वह magic अब काम नहीं करता। अपने tests को asynchronously चलाने पर, हम अपने test functions के अंदर TestClient का उपयोग नहीं कर सकते।

TestClient HTTPX पर आधारित है, और सौभाग्य से, हम API को test करने के लिए इसे सीधे उपयोग कर सकते हैं।

उदाहरण

एक सरल उदाहरण के लिए, आइए बड़ी Applications और Testing में वर्णित file structure जैसी एक structure पर विचार करें:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py

file main.py में यह होगा:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Tomato"}

file test_main.py में main.py के लिए tests होंगे, यह अब कुछ ऐसा दिख सकता है:

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"}

इसे चलाएँ

आप अपने tests को हमेशा की तरह इस तरह चला सकते हैं:

$ pytest

---> 100%

विस्तार से

marker @pytest.mark.anyio pytest को बताता है कि इस test function को asynchronously call किया जाना चाहिए:

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"}

सुझाव

ध्यान दें कि test function अब पहले की तरह TestClient का उपयोग करते समय केवल def नहीं, बल्कि async def है।

फिर हम app के साथ एक AsyncClient बना सकते हैं, और await का उपयोग करते हुए इसमें async requests भेज सकते हैं।

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"}

यह इसके बराबर है:

response = client.get('/')

...जिसका उपयोग हम TestClient के साथ अपनी requests बनाने के लिए करते थे।

सुझाव

ध्यान दें कि हम नए AsyncClient के साथ async/await का उपयोग कर रहे हैं - request asynchronous है।

चेतावनी

अगर आपकी application lifespan events पर निर्भर करती है, तो AsyncClient इन events को trigger नहीं करेगा। यह सुनिश्चित करने के लिए कि वे trigger हों, florimondmanca/asgi-lifespan से LifespanManager का उपयोग करें।

अन्य asynchronous function calls

क्योंकि testing function अब asynchronous है, आप अब अपने tests में अपनी FastAPI application को requests भेजने के अलावा अन्य async functions को भी call (और await) कर सकते हैं, ठीक वैसे ही जैसे आप उन्हें अपने code में कहीं और call करते हैं।

सुझाव

अगर अपने tests में asynchronous function calls integrate करते समय आपको RuntimeError: Task attached to a different loop मिलता है (जैसे MongoDB's MotorClient का उपयोग करते समय), तो याद रखें कि जिन objects को event loop की जरूरत होती है, उन्हें केवल async functions के भीतर ही instantiate करें, जैसे कि @app.on_event("startup") callback।