Uzziniet, kā efektīvi testēt savas FastAPI lietotnes, izmantojot TestClient. Ietver labāko praksi, uzlabotas metodes un reālus piemērus uzticamām API.
FastAPI Testēšanas apgūšana: Visaptverošs ceļvedis TestClient izmantošanai
FastAPI ir kļuvis par vadošo ietvaru augstas veiktspējas API izveidei ar Python. Tā ātrums, lietošanas ērtums un automātiskā datu validācija padara to par iecienītu izstrādātāju vidū visā pasaulē. Tomēr labi izveidota API ir tikpat laba, cik tās testi. Rūpīga testēšana nodrošina, ka jūsu API darbojas, kā paredzēts, saglabājas stabila zem spiediena un to var droši izvietot ražošanā. Šis visaptverošais ceļvedis koncentrējas uz FastAPI TestClient izmantošanu, lai efektīvi testētu jūsu API galapunktus.
Kāpēc testēšana ir svarīga FastAPI lietotnēm?
Testēšana ir būtisks solis programmatūras izstrādes ciklā. Tas palīdz jums:
- Identificēt kļūdas agri: Atklājiet kļūdas, pirms tās sasniedz ražošanu, ietaupot laiku un resursus.
- Nodrošināt koda kvalitāti: Veiciniet labi strukturētu un uzturējamu kodu.
- Novērst regresijas: Garantējiet, ka jaunās izmaiņas nesabojā esošo funkcionalitāti.
- Uzlabot API uzticamību: Veidojiet pārliecību par API stabilitāti un veiktspēju.
- Atvieglot sadarbību: Nodrošiniet skaidru dokumentāciju par paredzamo uzvedību citiem izstrādātājiem.
Iepazīstinām ar FastAPI TestClient
FastAPI nodrošina iebūvētu TestClient, kas vienkāršo jūsu API galapunktu testēšanas procesu. TestClient darbojas kā viegls klients, kas var sūtīt pieprasījumus uz jūsu API, nepalaižot pilnvērtīgu serveri. Tas padara testēšanu ievērojami ātrāku un ērtāku.
TestClient galvenās funkcijas:
- Simulē HTTP pieprasījumus: Ļauj jums sūtīt GET, POST, PUT, DELETE un citus HTTP pieprasījumus uz jūsu API.
- Apstrādā datu serializāciju: Automātiski serializē pieprasījuma datus (piemēram, JSON kravas) un deserializē atbildes datus.
- Nodrošina apgalvojumu metodes: Piedāvā ērtas metodes, lai pārbaudītu statusa kodu, galvenes un atbilžu saturu.
- Atbalsta asinhrono testēšanu: Bez problēmām darbojas ar FastAPI asinhrono dabu.
- Integrējas ar testēšanas ietvariem: Viegli integrējas ar populāriem Python testēšanas ietvariem, piemēram, pytest un unittest.
Testēšanas vides iestatīšana
Pirms sākat testēšanu, jums jāiestata testēšanas vide. Tas parasti ietver nepieciešamo atkarību instalēšanu un testēšanas ietvara konfigurēšanu.
Instalēšana
Pirmkārt, pārliecinieties, vai jums ir instalēts FastAPI un pytest. Varat tos instalēt, izmantojot pip:
pip install fastapi pytest httpx
httpx ir HTTP klients, ko FastAPI izmanto iekšēji. Lai gan TestClient ir daļa no FastAPI, httpx instalēšana arī nodrošina vienmērīgu testēšanu. Dažās pamācībās ir minēts arī requests, tomēr httpx vairāk atbilst FastAPI asinhronajai dabai.
FastAPI lietotnes piemērs
Izveidosim vienkāršu FastAPI lietotni, kuru varam izmantot testēšanai:
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
Saglabājiet šo kodu kā main.py. Šī lietotne definē trīs galapunktus:
/: Vienkāršs GET galapunkts, kas atgriež ziņojumu "Hello World"./items/{item_id}: GET galapunkts, kas atgriež vienumu, pamatojoties uz tā ID./items/: POST galapunkts, kas izveido jaunu vienumu.
Pirmā testa rakstīšana
Tagad, kad jums ir FastAPI lietotne, varat sākt rakstīt testus, izmantojot TestClient. Izveidojiet jaunu failu ar nosaukumu test_main.py tajā pašā direktorijā, kurā atrodas main.py.
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"}
Šajā testā:
- Mēs importējam
TestClientun FastAPIappinstanci. - Mēs izveidojam
TestClientinstanci, nododotapp. - Mēs definējam testa funkciju
test_read_root. - Testa funkcijas iekšpusē mēs izmantojam
client.get("/"), lai nosūtītu GET pieprasījumu uz saknes galapunktu. - Mēs apgalvojam, ka atbildes statusa kods ir 200 (OK).
- Mēs apgalvojam, ka atbildes JSON ir vienāds ar
{"message": "Hello World"}.
Testu palaišana ar pytest
Lai palaistu savus testus, vienkārši atveriet termināli direktorijā, kurā atrodas jūsu test_main.py fails, un palaidiet šādu komandu:
pytest
pytest automātiski atradīs un palaidīs visus testus jūsu projektā. Jums vajadzētu redzēt līdzīgu izvadi:
============================= 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 ===============================
Dažādu HTTP metožu testēšana
TestClient atbalsta visas standarta HTTP metodes, tostarp GET, POST, PUT, DELETE un PATCH. Apskatīsim, kā testēt katru no šīm metodēm.
GET pieprasījumu testēšana
Mēs jau redzējām GET pieprasījuma testēšanas piemēru iepriekšējā sadaļā. Šeit ir vēl viens piemērs, testējot /items/{item_id} galapunktu:
def test_read_item():
response = client.get("/items/1?q=test")
assert response.status_code == 200
assert response.json() == {"item_id": 1, "q": "test"}
Šis tests nosūta GET pieprasījumu uz /items/1 ar vaicājuma parametru q=test. Pēc tam tas apgalvo, ka atbildes statusa kods ir 200 un ka atbildes JSON satur paredzamos datus.
POST pieprasījumu testēšana
Lai testētu POST pieprasījumu, jums jānosūta dati pieprasījuma ķermenī. TestClient automātiski serializē datus uz JSON.
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
Šajā testā:
- Mēs izveidojam vārdnīcu
item_data, kas satur datus par jauno vienumu. - Mēs izmantojam
client.post("/items/", json=item_data), lai nosūtītu POST pieprasījumu uz/items/galapunktu, nododotitem_datakā JSON kravu. - Mēs apgalvojam, ka atbildes statusa kods ir 200 un ka atbildes JSON atbilst
item_data.
PUT, DELETE un PATCH pieprasījumu testēšana
PUT, DELETE un PATCH pieprasījumu testēšana ir līdzīga POST pieprasījumu testēšanai. Jūs vienkārši izmantojat atbilstošās metodes TestClient:
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
# Add assertions for the expected response
def test_delete_item():
response = client.delete("/items/1")
assert response.status_code == 200
# Add assertions for the expected response
def test_patch_item():
item_data = {"price": 29.99}
response = client.patch("/items/1", json=item_data)
assert response.status_code == 200
# Add assertions for the expected response
Atcerieties pievienot apgalvojumus, lai pārbaudītu, vai atbildes ir tādas, kā paredzēts.
Uzlabotas testēšanas metodes
TestClient piedāvā vairākas uzlabotas funkcijas, kas var palīdzēt rakstīt visaptverošākus un efektīvākus testus.
Testēšana ar atkarībām
FastAPI atkarību injekcijas sistēma ļauj viegli injicēt atkarības jūsu API galapunktos. Testējot, iespējams, vēlēsities ignorēt šīs atkarības, lai nodrošinātu ņirgāšanās vai testam specifiskus ieviešanas veidus.
Piemēram, pieņemsim, ka jūsu lietotne ir atkarīga no datu bāzes savienojuma. Jūs varat ignorēt datu bāzes atkarību savos testos, lai izmantotu atmiņā esošu datu bāzi:
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
# Database Configuration
DATABASE_URL = "sqlite:///./test.db" # In-memory database for testing
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# Define User Model
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 App
app = FastAPI()
# Dependency to get the database session
def get_db():
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
# Endpoint to create a user
@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)
# Override the database dependency for testing
def override_get_db():
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
def test_create_user():
# First, ensure the tables are created, which may not happen by default
Base.metadata.create_all(bind=engine) # important: create the tables in the test db
response = client.post("/users/", params={"username": "testuser", "password": "password123"})
assert response.status_code == 200
assert response.json()["username"] == "testuser"
# Clean up the override after the test if needed
app.dependency_overrides = {}
Šis piemērs ignorē get_db atkarību ar testam specifisku funkciju, kas atgriež sesiju atmiņā esošai SQLite datu bāzei. Svarīgi: Lai testa datu bāze darbotos pareizi, ir skaidri jāizsauc metadatu izveide. Ja tabula netiks izveidota, radīsies kļūdas, kas saistītas ar trūkstošām tabulām.
Asinhronā koda testēšana
FastAPI ir veidota kā asinhrona, tāpēc jums bieži būs jātestē asinhronais kods. TestClient nemanāmi atbalsta asinhrono testēšanu.
Lai testētu asinhrono galapunktu, vienkārši definējiet savu testa funkciju kā async:
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.get("/async")
async def async_endpoint():
await asyncio.sleep(0.1) # Simulate some async operation
return {"message": "Async Hello"}
import pytest
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
@pytest.mark.asyncio # Needed to be compatible with pytest-asyncio
async def test_async_endpoint():
response = client.get("/async")
assert response.status_code == 200
assert response.json() == {"message": "Async Hello"}
Piezīme: Lai izmantotu @pytest.mark.asyncio, jums ir jāinstalē pytest-asyncio: pip install pytest-asyncio. Jums arī jānodrošina, ka ir konfigurēts asyncio.get_event_loop(), ja izmantojat vecākas pytest versijas. Ja izmantojat pytest 8 vai jaunāku versiju, tas var nebūt nepieciešams.
Failu augšupielādes testēšana
FastAPI atvieglo failu augšupielādi. Lai testētu failu augšupielādi, varat izmantot TestClient pieprasījuma metožu parametru files.
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"]}
Šajā testā mēs izveidojam fiktīvu failu, izmantojot io.BytesIO, un nododam to parametram files. Parametrs files pieņem kortežu sarakstu, kur katrs kortežs satur lauka nosaukumu, faila nosaukumu un faila saturu. Satura veids ir svarīgs, lai serveris to precīzi apstrādātu.
Kļūdu apstrādes testēšana
Ir svarīgi testēt, kā jūsu API apstrādā kļūdas. Varat izmantot TestClient, lai nosūtītu nederīgus pieprasījumus un pārbaudītu, vai API atgriež pareizās kļūdu atbildes.
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"}
Šis tests nosūta GET pieprasījumu uz /items/101, kas izraisa HTTPException ar statusa kodu 400. Tests apgalvo, ka atbildes statusa kods ir 400 un ka atbildes JSON satur paredzamo kļūdas ziņojumu.
Drošības līdzekļu testēšana
Ja jūsu API izmanto autentifikāciju vai autorizāciju, jums būs jātestē arī šie drošības līdzekļi. TestClient ļauj iestatīt galvenes un sīkdatnes, lai simulētu autentificētus pieprasījumus.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
app = FastAPI()
# Security
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Simulate authentication
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():
# First, get a token
token_response = client.post("/token", data={"username": "testuser", "password": "password123"})
token = token_response.json()["access_token"]
# Then, use the token to access the protected route
response = client.get("/protected", headers={"Authorization": f"Bearer {token}"}) # corrected format.
assert response.status_code == 200
assert response.json() == {"message": "Protected data"}
Šajā piemērā mēs testējam pieteikšanās galapunktu un pēc tam izmantojam saņemto tokeni, lai piekļūtu aizsargātai maršruta kartei. Parametrs headers TestClient pieprasījuma metodēm ļauj iestatīt pielāgotas galvenes, tostarp galveni Authorization nesēja tokeniem.
Labākā prakse FastAPI testēšanai
Šeit ir daži labākās prakses ieteikumi, kas jāievēro, testējot savas FastAPI lietotnes:
- Rakstiet visaptverošus testus: Tiecieties uz augstu testa pārklājumu, lai nodrošinātu, ka visas jūsu API daļas ir rūpīgi pārbaudītas.
- Izmantojiet aprakstošus testu nosaukumus: Pārliecinieties, vai jūsu testu nosaukumi skaidri norāda, ko tests pārbauda.
- Ievērojiet modeli Sakārtot-Darboties-Apgalvot: Organizējiet savus testus trīs atšķirīgās fāzēs: Sakārtot (iestatiet testa datus), Darboties (veiciet testējamo darbību) un Apgalvot (pārbaudiet rezultātus).
- Izmantojiet ņirgāšanās objektus: Ierobežojiet ārējās atkarības, lai izolētu savus testus un izvairītos no paļaušanās uz ārējām sistēmām.
- Testējiet robežgadījumus: Testējiet savu API ar nederīgu vai negaidītu ievadi, lai nodrošinātu, ka tā graciozi apstrādā kļūdas.
- Palaidiet testus bieži: Integrējiet testēšanu savā izstrādes darbplūsmā, lai savlaicīgi un bieži atklātu kļūdas.
- Integrējiet ar CI/CD: Automatizējiet savus testus savā CI/CD cauruļvadā, lai nodrošinātu, ka visas koda izmaiņas tiek rūpīgi pārbaudītas pirms izvietošanas ražošanā. Lai to panāktu, var izmantot tādus rīkus kā Jenkins, GitLab CI, GitHub Actions vai CircleCI.
Piemērs: Internacionalizācijas (i18n) testēšana
Izstrādājot API globālai auditorijai, internacionalizācija (i18n) ir būtiska. I18n testēšana ietver pārliecināšanos, vai jūsu API pareizi atbalsta vairākas valodas un reģionus. Šeit ir piemērs, kā varat testēt i18n FastAPI lietotnē:
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!"}
Šis piemērs iestata galveni Accept-Language, lai norādītu vēlamo valodu. API atgriež sveicienu norādītajā valodā. Testēšana nodrošina, ka API pareizi apstrādā dažādas valodu preferences. Ja galvene Accept-Language nav norādīta, tiek izmantota noklusējuma valoda "en".
Secinājums
Testēšana ir būtiska daļa no spēcīgu un uzticamu FastAPI lietotņu izveides. TestClient nodrošina vienkāršu un ērtu veidu, kā testēt savus API galapunktus. Ievērojot šajā rokasgrāmatā izklāstīto labāko praksi, varat rakstīt visaptverošus testus, kas nodrošina jūsu API kvalitāti un stabilitāti. Sākot no pamata pieprasījumiem līdz uzlabotām metodēm, piemēram, atkarību injekcijai un asinhronai testēšanai, TestClient ļauj jums izveidot labi pārbaudītu un uzturējamu kodu. Iekļaujiet testēšanu kā galveno savas izstrādes darbplūsmas daļu, un jūs izveidosit API, kas ir gan jaudīgas, gan uzticamas lietotājiem visā pasaulē. Atcerieties CI/CD integrācijas nozīmi, lai automatizētu testēšanu un nodrošinātu nepārtrauktu kvalitātes nodrošināšanu.