TestClient yordamida FastAPI ilovalaringizni samarali testlashni o'rganing. Mustahkam APIlar uchun ilg'or amaliyotlar, texnikalar va real misollarni qamrab oladi.
FastAPI Testlashni O'zlashtirish: TestClient uchun To'liq Qo'llanma
FastAPI Python yordamida yuqori unumdorlikka ega APIlar yaratish uchun yetakchi freymvorklardan biriga aylandi. Uning tezligi, foydalanish osonligi va ma'lumotlarni avtomatik tasdiqlashi uni butun dunyo bo'ylab dasturchilar orasida sevimli vositaga aylantirdi. Biroq, yaxshi qurilgan API faqat uning testlari kabi yaxshidir. Puxta testlash sizning API'ngiz kutilganidek ishlashini, bosim ostida barqaror bo'lishini va ishonch bilan production'ga joylashtirilishini ta'minlaydi. Ushbu to'liq qo'llanma API endpoint'laringizni samarali testlash uchun FastAPI'ning TestClient vositasidan foydalanishga qaratilgan.
Nima uchun FastAPI Ilovalari uchun Testlash Muhim?
Testlash dasturiy ta'minotni ishlab chiqish hayotiy siklidagi hal qiluvchi qadamdir. U sizga yordam beradi:
- Xatoliklarni erta aniqlash: Xatolarni production'ga chiqishidan oldin topib, vaqt va resurslarni tejash.
- Kod sifatini ta'minlash: Yaxshi tuzilgan va qo'llab-quvvatlanadigan kodni rag'batlantirish.
- Regressiyalarni oldini olish: Yangi o'zgarishlar mavjud funksionallikni buzmasligini kafolatlash.
- API ishonchliligini oshirish: API'ning barqarorligi va unumdorligiga ishonchni mustahkamlash.
- Hamkorlikni osonlashtirish: Boshqa dasturchilar uchun kutilayotgan xatti-harakatlar haqida aniq hujjatlarni taqdim etish.
FastAPI'ning TestClient'i bilan tanishuv
FastAPI API endpoint'laringizni testlash jarayonini soddalashtiradigan o'rnatilgan TestClient'ni taqdim etadi. TestClient to'liq serverni ishga tushirmasdan API'ngizga so'rovlar yuborishi mumkin bo'lgan yengil klient vazifasini bajaradi. Bu testlashni ancha tezroq va qulayroq qiladi.
TestClient'ning Asosiy Xususiyatlari:
- HTTP so'rovlarini simulyatsiya qiladi: API'ngizga GET, POST, PUT, DELETE va boshqa HTTP so'rovlarini yuborish imkonini beradi.
- Ma'lumotlarni seriyalashni boshqaradi: So'rov ma'lumotlarini (masalan, JSON yuklamalari) avtomatik seriyalashtiradi va javob ma'lumotlarini deseriyalashtiradi.
- Tasdiqlash (assertion) metodlarini taqdim etadi: Javoblarning status kodi, sarlavhalari va mazmunini tekshirish uchun qulay metodlarni taklif etadi.
- Asinxron testlashni qo'llab-quvvatlaydi: FastAPI'ning asinxron tabiati bilan muammosiz ishlaydi.
- Testlash freymvorklari bilan integratsiyalashadi: pytest va unittest kabi mashhur Python testlash freymvorklari bilan oson integratsiyalashadi.
Testlash Muhitini Sozlash
Testlashni boshlashdan oldin, siz testlash muhitini sozlashingiz kerak. Bu odatda kerakli bog'liqliklarni o'rnatish va testlash freymvorkini sozlashni o'z ichiga oladi.
O'rnatish
Avvalo, FastAPI va pytest o'rnatilganligiga ishonch hosil qiling. Ularni pip yordamida o'rnatishingiz mumkin:
pip install fastapi pytest httpx
httpx - bu FastAPI ichkarida foydalanadigan HTTP klient. TestClient FastAPI'ning bir qismi bo'lsa-da, httpx'ni ham o'rnatish testlashning silliq o'tishini ta'minlaydi. Ba'zi qo'llanmalarda requests ham tilga olinadi, ammo httpx FastAPI'ning asinxron tabiatiga ko'proq mos keladi.
Misol FastAPI Ilovasi
Keling, testlash uchun foydalanishimiz mumkin bo'lgan oddiy FastAPI ilovasini yaratamiz:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.get("/")
async def read_root():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.post("/items/")
async def create_item(item: Item):
return item
Ushbu kodni main.py sifatida saqlang. Bu ilova uchta endpoint'ni belgilaydi:
/: "Hello World" xabarini qaytaradigan oddiy GET endpoint./items/{item_id}: ID'si bo'yicha elementni qaytaradigan GET endpoint./items/: Yangi element yaratadigan POST endpoint.
Birinchi Testingizni Yozish
Endi sizda FastAPI ilovasi bor, siz TestClient yordamida testlar yozishni boshlashingiz mumkin. main.py bilan bir xil katalogda test_main.py nomli yangi fayl yarating.
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
Ushbu testda:
- Biz
TestClientva FastAPIappnusxasini import qilamiz. - Biz
app'ni uzatib,TestClientnusxasini yaratamiz. - Biz
test_read_roottest funksiyasini belgilaymiz. - Test funksiyasi ichida, biz root endpoint'ga GET so'rovini yuborish uchun
client.get("/")dan foydalanamiz. - Biz javob status kodi 200 (OK) ekanligini tasdiqlaymiz.
- Biz javob JSON'i
{"message": "Hello World"}ga teng ekanligini tasdiqlaymiz.
Testlarni pytest bilan Ishga Tushirish
Testlaringizni ishga tushirish uchun, shunchaki test_main.py faylingiz joylashgan katalogda terminalni oching va quyidagi buyruqni bajaring:
pytest
pytest loyihangizdagi barcha testlarni avtomatik ravishda topadi va ishga tushiradi. Siz quyidagiga o'xshash natijani ko'rishingiz kerak:
============================= test session starts ==============================
platform darwin -- Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
collected 1 item
test_main.py .
============================== 1 passed in 0.01s ===============================
Turli xil HTTP Metodlarini Testlash
TestClient GET, POST, PUT, DELETE va PATCH kabi barcha standart HTTP metodlarini qo'llab-quvvatlaydi. Keling, ushbu metodlarning har birini qanday testlashni ko'rib chiqamiz.
GET So'rovlarini Testlash
Biz oldingi bo'limda GET so'rovini testlash misolini ko'rdik. Mana yana bir misol, /items/{item_id} endpoint'ini testlash:
def test_read_item():
response = client.get("/items/1?q=test")
assert response.status_code == 200
assert response.json() == {"item_id": 1, "q": "test"}
Ushbu test /items/1 manziliga q=test so'rov parametri bilan GET so'rovini yuboradi. Keyin javob status kodi 200 ekanligini va javob JSON'i kutilgan ma'lumotlarni o'z ichiga olganligini tasdiqlaydi.
POST So'rovlarini Testlash
POST so'rovini testlash uchun siz so'rov tanasida ma'lumot yuborishingiz kerak. TestClient ma'lumotlarni avtomatik ravishda JSON formatiga seriyalashtiradi.
def test_create_item():
item_data = {"name": "Example Item", "description": "A test item", "price": 9.99, "tax": 1.00}
response = client.post("/items/", json=item_data)
assert response.status_code == 200
assert response.json() == item_data
Ushbu testda:
- Biz yangi element uchun ma'lumotlarni o'z ichiga olgan
item_datalug'atini yaratamiz. - Biz
/items/endpoint'iga POST so'rovini yuborish uchunclient.post("/items/", json=item_data)dan foydalanamiz vaitem_data'ni JSON yuklamasi sifatida uzatamiz. - Biz javob status kodi 200 ekanligini va javob JSON'i
item_databilan mos kelishini tasdiqlaymiz.
PUT, DELETE va PATCH So'rovlarini Testlash
PUT, DELETE va PATCH so'rovlarini testlash POST so'rovlarini testlashga o'xshaydi. Siz shunchaki TestClient'da mos keladigan metodlardan foydalanasiz:
def test_update_item():
item_data = {"name": "Updated Item", "description": "An updated test item", "price": 19.99, "tax": 2.00}
response = client.put("/items/1", json=item_data)
assert response.status_code == 200
# Kutilgan javob uchun tasdiqlashlarni qo'shing
def test_delete_item():
response = client.delete("/items/1")
assert response.status_code == 200
# Kutilgan javob uchun tasdiqlashlarni qo'shing
def test_patch_item():
item_data = {"price": 29.99}
response = client.patch("/items/1", json=item_data)
assert response.status_code == 200
# Kutilgan javob uchun tasdiqlashlarni qo'shing
Javoblar kutilganidek ekanligini tekshirish uchun tasdiqlashlarni (assertions) qo'shishni unutmang.
Ilg'or Testlash Texnikalari
TestClient sizga yanada keng qamrovli va samarali testlar yozishga yordam beradigan bir nechta ilg'or xususiyatlarni taklif etadi.
Bog'liqliklar bilan Testlash
FastAPI'ning bog'liqliklarni kiritish (dependency injection) tizimi sizga API endpoint'laringizga bog'liqliklarni osonlikcha kiritish imkonini beradi. Testlash paytida, siz ushbu bog'liqliklarni soxta (mock) yoki testga xos implementatsiyalar bilan almashtirishni xohlashingiz mumkin.
Masalan, ilovangiz ma'lumotlar bazasiga ulanishga bog'liq deb faraz qilaylik. Siz testlaringizda xotiradagi (in-memory) ma'lumotlar bazasidan foydalanish uchun ma'lumotlar bazasi bog'liqligini almashtirishingiz mumkin:
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base, Session
# Ma'lumotlar bazasi konfiguratsiyasi
DATABASE_URL = "sqlite:///./test.db" # Testlash uchun xotiradagi ma'lumotlar bazasi
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# User Modelini aniqlash
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
password = Column(String)
Base.metadata.create_all(bind=engine)
# FastAPI ilovasi
app = FastAPI()
# Ma'lumotlar bazasi sessiyasini olish uchun bog'liqlik
def get_db():
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
# Foydalanuvchi yaratish uchun endpoint
@app.post("/users/")
async def create_user(username: str, password: str, db: Session = Depends(get_db)):
db_user = User(username=username, password=password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
from fastapi.testclient import TestClient
from .main import app, get_db, Base, engine, TestingSessionLocal
client = TestClient(app)
# Testlash uchun ma'lumotlar bazasi bog'liqligini almashtirish
def override_get_db():
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
def test_create_user():
# Avvalo, jadvallar yaratilganligiga ishonch hosil qiling, bu sukut bo'yicha yuz bermasligi mumkin
Base.metadata.create_all(bind=engine) # muhim: test ma'lumotlar bazasida jadvallarni yarating
response = client.post("/users/", params={"username": "testuser", "password": "password123"})
assert response.status_code == 200
assert response.json()["username"] == "testuser"
# Testdan so'ng almashtirishni tozalang, agar kerak bo'lsa
app.dependency_overrides = {}
Ushbu misol get_db bog'liqligini xotiradagi SQLite ma'lumotlar bazasiga sessiya qaytaradigan testga xos funksiya bilan almashtiradi. Muhim: Test ma'lumotlar bazasi to'g'ri ishlashi uchun metama'lumotlarni yaratish aniq chaqirilishi kerak. Jadvalni yaratmaslik, jadvallar yo'qligi bilan bog'liq xatoliklarga olib keladi.
Asinxron Kodni Testlash
FastAPI asinxron bo'lishi uchun yaratilgan, shuning uchun siz tez-tez asinxron kodni testlashingiz kerak bo'ladi. TestClient asinxron testlashni muammosiz qo'llab-quvvatlaydi.
Asinxron endpoint'ni testlash uchun, shunchaki test funksiyangizni async sifatida belgilang:
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.get("/async")
async def async_endpoint():
await asyncio.sleep(0.1) # Qandaydir asinxron operatsiyani simulyatsiya qilish
return {"message": "Async Hello"}
import pytest
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
@pytest.mark.asyncio # pytest-asyncio bilan mos bo'lishi uchun kerak
async def test_async_endpoint():
response = client.get("/async")
assert response.status_code == 200
assert response.json() == {"message": "Async Hello"}
Eslatma: @pytest.mark.asyncio'dan foydalanish uchun pytest-asyncio'ni o'rnatishingiz kerak: pip install pytest-asyncio. Agar eski pytest versiyalaridan foydalanayotgan bo'lsangiz, asyncio.get_event_loop() sozlanishini ham ta'minlashingiz kerak. Agar pytest 8 yoki undan yangi versiyadan foydalanayotgan bo'lsangiz, bu talab qilinmasligi mumkin.
Fayl Yuklashlarni Testlash
FastAPI fayl yuklashlarni boshqarishni osonlashtiradi. Fayl yuklashlarni testlash uchun siz TestClient'ning so'rov metodlarining files parametrlaridan foydalanishingiz mumkin.
from fastapi import FastAPI, File, UploadFile
from typing import List
app = FastAPI()
@app.post("/files/")
async def create_files(files: List[bytes] = File()):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile]):
return {"filenames": [file.filename for file in files]}
from fastapi.testclient import TestClient
from .main import app
import io
client = TestClient(app)
def test_create_files():
file_content = b"Test file content"
files = [('files', ('test.txt', io.BytesIO(file_content), 'text/plain'))]
response = client.post("/files/", files=files)
assert response.status_code == 200
assert response.json() == {"file_sizes": [len(file_content)]}
def test_create_upload_files():
file_content = b"Test upload file content"
files = [('files', ('test_upload.txt', io.BytesIO(file_content), 'text/plain'))]
response = client.post("/uploadfiles/", files=files)
assert response.status_code == 200
assert response.json() == {"filenames": ["test_upload.txt"]}
Ushbu testda biz io.BytesIO yordamida soxta fayl yaratamiz va uni files parametriga uzatamiz. files parametri kortejlar ro'yxatini qabul qiladi, bu yerda har bir kortej maydon nomi, fayl nomi va fayl tarkibini o'z ichiga oladi. Kontent turi server tomonidan to'g'ri ishlov berilishi uchun muhimdir.
Xatoliklarni Ishlashni Testlash
API'ngiz xatoliklarni qanday boshqarishini testlash muhim. Siz TestClient yordamida noto'g'ri so'rovlar yuborishingiz va API to'g'ri xato javoblarini qaytarishini tekshirishingiz mumkin.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id > 100:
raise HTTPException(status_code=400, detail="Item ID too large")
return {"item_id": item_id}
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item_error():
response = client.get("/items/101")
assert response.status_code == 400
assert response.json() == {"detail": "Item ID too large"}
Ushbu test /items/101 manziliga GET so'rovini yuboradi, bu esa 400 status kodi bilan HTTPException'ni keltirib chiqaradi. Test javob status kodi 400 ekanligini va javob JSON'i kutilgan xato xabarini o'z ichiga olganligini tasdiqlaydi.
Xavfsizlik Xususiyatlarini Testlash
Agar sizning API'ngiz autentifikatsiya yoki avtorizatsiyadan foydalansa, siz ushbu xavfsizlik xususiyatlarini ham testlashingiz kerak. TestClient autentifikatsiyadan o'tgan so'rovlarni simulyatsiya qilish uchun sarlavhalar va cookie'larni o'rnatishga imkon beradi.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
app = FastAPI()
# Xavfsizlik
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Autentifikatsiyani simulyatsiya qilish
if form_data.username != "testuser" or form_data.password != "password123":
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password")
return {"access_token": "fake_token", "token_type": "bearer"}
@app.get("/protected")
async def protected_route(token: str = Depends(oauth2_scheme)):
return {"message": "Protected data"}
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_login():
response = client.post("/token", data={"username": "testuser", "password": "password123"})
assert response.status_code == 200
assert "access_token" in response.json()
def test_protected_route():
# Avval, token oling
token_response = client.post("/token", data={"username": "testuser", "password": "password123"})
token = token_response.json()["access_token"]
# Keyin, himoyalangan yo'nalishga kirish uchun tokendan foydalaning
response = client.get("/protected", headers={"Authorization": f"Bearer {token}"}) # tuzatilgan format.
assert response.status_code == 200
assert response.json() == {"message": "Protected data"}
Ushbu misolda biz kirish endpoint'ini testlaymiz va keyin olingan tokendan himoyalangan yo'nalishga kirish uchun foydalanamiz. TestClient'ning so'rov metodlarining headers parametri sizga maxsus sarlavhalarni, shu jumladan bearer tokenlar uchun Authorization sarlavhasini o'rnatishga imkon beradi.
FastAPI Testlash uchun Eng Yaxshi Amaliyotlar
FastAPI ilovalaringizni testlashda quyidagi eng yaxshi amaliyotlarga rioya qiling:
- Keng qamrovli testlar yozing: API'ngizning barcha qismlari puxta testlanganligiga ishonch hosil qilish uchun yuqori test qamroviga intiling.
- Tavsiflovchi test nomlaridan foydalaning: Test nomlaringiz test nimani tekshirayotganini aniq ko'rsatishiga ishonch hosil qiling.
- Arrange-Act-Assert naqshiga rioya qiling: Testlaringizni uchta aniq bosqichga ajrating: Arrange (test ma'lumotlarini sozlash), Act (test qilinayotgan amalni bajarish) va Assert (natijalarni tekshirish).
- Soxta (mock) obyektlardan foydalaning: Testlaringizni izolyatsiya qilish va tashqi tizimlarga bog'liq bo'lmaslik uchun tashqi bog'liqliklarni soxtalashtiring.
- Chekka holatlarni (edge cases) testlang: API'ngiz xatolarni to'g'ri boshqarishiga ishonch hosil qilish uchun uni noto'g'ri yoki kutilmagan kirish ma'lumotlari bilan testlang.
- Testlarni tez-tez ishga tushiring: Xatolarni erta va tez-tez topish uchun testlashni ishlab chiqish jarayoniga integratsiya qiling.
- CI/CD bilan integratsiya qiling: Barcha kod o'zgarishlari production'ga joylashtirilishidan oldin puxta testlanganligiga ishonch hosil qilish uchun testlaringizni CI/CD quvuringizda avtomatlashtiring. Bunga erishish uchun Jenkins, GitLab CI, GitHub Actions yoki CircleCI kabi vositalardan foydalanish mumkin.
Misol: Xalqarolashtirish (i18n) Testlash
Global auditoriya uchun APIlar ishlab chiqishda xalqarolashtirish (i18n) muhim ahamiyatga ega. i18n'ni testlash API'ngiz bir nechta til va mintaqalarni to'g'ri qo'llab-quvvatlashini tekshirishni o'z ichiga oladi. Mana FastAPI ilovasida i18n'ni qanday testlash mumkinligi haqida misol:
from fastapi import FastAPI, Header
from typing import Optional
app = FastAPI()
messages = {
"en": {"greeting": "Hello, world!"},
"fr": {"greeting": "Bonjour le monde !"},
"es": {"greeting": "¡Hola Mundo!"},
}
@app.get("/")
async def read_root(accept_language: Optional[str] = Header(None)):
lang = accept_language[:2] if accept_language else "en"
if lang not in messages:
lang = "en"
return {"message": messages[lang]["greeting"]}
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_root_en():
response = client.get("/", headers={"Accept-Language": "en-US"})
assert response.status_code == 200
assert response.json() == {"message": "Hello, world!"}
def test_read_root_fr():
response = client.get("/", headers={"Accept-Language": "fr-FR"})
assert response.status_code == 200
assert response.json() == {"message": "Bonjour le monde !"}
def test_read_root_es():
response = client.get("/", headers={"Accept-Language": "es-ES"})
assert response.status_code == 200
assert response.json() == {"message": "¡Hola Mundo!"}
def test_read_root_default():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello, world!"}
Ushbu misol kerakli tilni belgilash uchun Accept-Language sarlavhasini o'rnatadi. API salomlashuvni belgilangan tilda qaytaradi. Testlash API'ning turli til afzalliklarini to'g'ri boshqarishini ta'minlaydi. Agar Accept-Language sarlavhasi mavjud bo'lmasa, sukut bo'yicha "en" tili ishlatiladi.
Xulosa
Testlash mustahkam va ishonchli FastAPI ilovalarini yaratishning muhim qismidir. TestClient API endpoint'laringizni testlashning oddiy va qulay usulini taqdim etadi. Ushbu qo'llanmada keltirilgan eng yaxshi amaliyotlarga rioya qilish orqali siz APIlaringizning sifati va barqarorligini ta'minlaydigan keng qamrovli testlar yozishingiz mumkin. Asosiy so'rovlardan tortib, bog'liqliklarni kiritish va asinxron testlash kabi ilg'or texnikalargacha, TestClient sizga yaxshi testlangan va qo'llab-quvvatlanadigan kod yaratish imkonini beradi. Testlashni ishlab chiqish jarayonining asosiy qismi sifatida qabul qiling va siz butun dunyo bo'ylab foydalanuvchilar uchun ham kuchli, ham ishonchli APIlar yaratasiz. Testlashni avtomatlashtirish va doimiy sifat nazoratini ta'minlash uchun CI/CD integratsiyasining ahamiyatini unutmang.