Movatterモバイル変換


[0]ホーム

URL:


Saltar a contenido
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

Testing

🌐 Traducción por IA y humanos

Esta traducción fue hecha por IA guiada por humanos. 🤝

Podría tener errores al interpretar el significado original, o sonar poco natural, etc. 🤖

Puedes mejorar esta traducciónayudándonos a guiar mejor al LLM de IA.

Versión en inglés

Gracias aStarlette, escribir pruebas para aplicaciones deFastAPI es fácil y agradable.

Está basado enHTTPX, que a su vez está diseñado basado en Requests, por lo que es muy familiar e intuitivo.

Con él, puedes usarpytest directamente conFastAPI.

UsandoTestClient

Información

Para usarTestClient, primero instalahttpx.

Asegúrate de crear unentorno virtual, activarlo y luego instalarlo, por ejemplo:

$pipinstallhttpx

ImportaTestClient.

Crea unTestClient pasándole tu aplicación deFastAPI.

Crea funciones con un nombre que comience contest_ (esta es la convención estándar depytest).

Usa el objetoTestClient de la misma manera que conhttpx.

Escribe declaracionesassert simples con las expresiones estándar de Python que necesites revisar (otra vez, estándar depytest).

fromfastapiimportFastAPIfromfastapi.testclientimportTestClientapp=FastAPI()@app.get("/")asyncdefread_main():return{"msg":"Hello World"}client=TestClient(app)deftest_read_main():response=client.get("/")assertresponse.status_code==200assertresponse.json()=={"msg":"Hello World"}

Consejo

Nota que las funciones de prueba sondef normales, noasync def.

Y las llamadas al cliente también son llamadas normales, sin usarawait.

Esto te permite usarpytest directamente sin complicaciones.

Nota Técnica

También podrías usarfrom starlette.testclient import TestClient.

FastAPI proporciona el mismostarlette.testclient comofastapi.testclient solo por conveniencia para ti, el desarrollador. Pero proviene directamente de Starlette.

Consejo

Si quieres llamar a funcionesasync en tus pruebas además de enviar requests a tu aplicación FastAPI (por ejemplo, funciones asincrónicas de bases de datos), echa un vistazo a lasPruebas Asincrónicas en el tutorial avanzado.

Separando pruebas

En una aplicación real, probablemente tendrías tus pruebas en un archivo diferente.

Y tu aplicación deFastAPI también podría estar compuesta de varios archivos/módulos, etc.

Archivo de aplicaciónFastAPI

Digamos que tienes una estructura de archivos como se describe enAplicaciones Más Grandes:

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

En el archivomain.py tienes tu aplicación deFastAPI:

fromfastapiimportFastAPIapp=FastAPI()@app.get("/")asyncdefread_main():return{"msg":"Hello World"}

Archivo de prueba

Entonces podrías tener un archivotest_main.py con tus pruebas. Podría estar en el mismo paquete de Python (el mismo directorio con un archivo__init__.py):

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

Debido a que este archivo está en el mismo paquete, puedes usar importaciones relativas para importar el objetoapp desde el módulomain (main.py):

fromfastapi.testclientimportTestClientfrom.mainimportappclient=TestClient(app)deftest_read_main():response=client.get("/")assertresponse.status_code==200assertresponse.json()=={"msg":"Hello World"}

...y tener el código para las pruebas tal como antes.

Pruebas: ejemplo extendido

Ahora extiende este ejemplo y añade más detalles para ver cómo escribir pruebas para diferentes partes.

Archivo de aplicaciónFastAPI extendido

Continuemos con la misma estructura de archivos que antes:

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

Digamos que ahora el archivomain.py con tu aplicación deFastAPI tiene algunas otraspath operations.

Tiene una operaciónGET que podría devolver un error.

Tiene una operaciónPOST que podría devolver varios errores.

Ambaspath operations requieren unX-Token header.

fromtypingimportAnnotatedfromfastapiimportFastAPI,Header,HTTPExceptionfrompydanticimportBaseModelfake_secret_token="coneofsilence"fake_db={"foo":{"id":"foo","title":"Foo","description":"There goes my hero"},"bar":{"id":"bar","title":"Bar","description":"The bartenders"},}app=FastAPI()classItem(BaseModel):id:strtitle:strdescription:str|None=None@app.get("/items/{item_id}",response_model=Item)asyncdefread_main(item_id:str,x_token:Annotated[str,Header()]):ifx_token!=fake_secret_token:raiseHTTPException(status_code=400,detail="Invalid X-Token header")ifitem_idnotinfake_db:raiseHTTPException(status_code=404,detail="Item not found")returnfake_db[item_id]@app.post("/items/")asyncdefcreate_item(item:Item,x_token:Annotated[str,Header()])->Item:ifx_token!=fake_secret_token:raiseHTTPException(status_code=400,detail="Invalid X-Token header")ifitem.idinfake_db:raiseHTTPException(status_code=409,detail="Item already exists")fake_db[item.id]=item.model_dump()returnitem
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

fromfastapiimportFastAPI,Header,HTTPExceptionfrompydanticimportBaseModelfake_secret_token="coneofsilence"fake_db={"foo":{"id":"foo","title":"Foo","description":"There goes my hero"},"bar":{"id":"bar","title":"Bar","description":"The bartenders"},}app=FastAPI()classItem(BaseModel):id:strtitle:strdescription:str|None=None@app.get("/items/{item_id}",response_model=Item)asyncdefread_main(item_id:str,x_token:str=Header()):ifx_token!=fake_secret_token:raiseHTTPException(status_code=400,detail="Invalid X-Token header")ifitem_idnotinfake_db:raiseHTTPException(status_code=404,detail="Item not found")returnfake_db[item_id]@app.post("/items/")asyncdefcreate_item(item:Item,x_token:str=Header())->Item:ifx_token!=fake_secret_token:raiseHTTPException(status_code=400,detail="Invalid X-Token header")ifitem.idinfake_db:raiseHTTPException(status_code=409,detail="Item already exists")fake_db[item.id]=item.model_dump()returnitem

Archivo de prueba extendido

Podrías entonces actualizartest_main.py con las pruebas extendidas:

fromfastapi.testclientimportTestClientfrom.mainimportappclient=TestClient(app)deftest_read_item():response=client.get("/items/foo",headers={"X-Token":"coneofsilence"})assertresponse.status_code==200assertresponse.json()=={"id":"foo","title":"Foo","description":"There goes my hero",}deftest_read_item_bad_token():response=client.get("/items/foo",headers={"X-Token":"hailhydra"})assertresponse.status_code==400assertresponse.json()=={"detail":"Invalid X-Token header"}deftest_read_nonexistent_item():response=client.get("/items/baz",headers={"X-Token":"coneofsilence"})assertresponse.status_code==404assertresponse.json()=={"detail":"Item not found"}deftest_create_item():response=client.post("/items/",headers={"X-Token":"coneofsilence"},json={"id":"foobar","title":"Foo Bar","description":"The Foo Barters"},)assertresponse.status_code==200assertresponse.json()=={"id":"foobar","title":"Foo Bar","description":"The Foo Barters",}deftest_create_item_bad_token():response=client.post("/items/",headers={"X-Token":"hailhydra"},json={"id":"bazz","title":"Bazz","description":"Drop the bazz"},)assertresponse.status_code==400assertresponse.json()=={"detail":"Invalid X-Token header"}deftest_create_existing_item():response=client.post("/items/",headers={"X-Token":"coneofsilence"},json={"id":"foo","title":"The Foo ID Stealers","description":"There goes my stealer",},)assertresponse.status_code==409assertresponse.json()=={"detail":"Item already exists"}
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

fromfastapi.testclientimportTestClientfrom.mainimportappclient=TestClient(app)deftest_read_item():response=client.get("/items/foo",headers={"X-Token":"coneofsilence"})assertresponse.status_code==200assertresponse.json()=={"id":"foo","title":"Foo","description":"There goes my hero",}deftest_read_item_bad_token():response=client.get("/items/foo",headers={"X-Token":"hailhydra"})assertresponse.status_code==400assertresponse.json()=={"detail":"Invalid X-Token header"}deftest_read_nonexistent_item():response=client.get("/items/baz",headers={"X-Token":"coneofsilence"})assertresponse.status_code==404assertresponse.json()=={"detail":"Item not found"}deftest_create_item():response=client.post("/items/",headers={"X-Token":"coneofsilence"},json={"id":"foobar","title":"Foo Bar","description":"The Foo Barters"},)assertresponse.status_code==200assertresponse.json()=={"id":"foobar","title":"Foo Bar","description":"The Foo Barters",}deftest_create_item_bad_token():response=client.post("/items/",headers={"X-Token":"hailhydra"},json={"id":"bazz","title":"Bazz","description":"Drop the bazz"},)assertresponse.status_code==400assertresponse.json()=={"detail":"Invalid X-Token header"}deftest_create_existing_item():response=client.post("/items/",headers={"X-Token":"coneofsilence"},json={"id":"foo","title":"The Foo ID Stealers","description":"There goes my stealer",},)assertresponse.status_code==409assertresponse.json()=={"detail":"Item already exists"}

Cada vez que necesites que el cliente pase información en el request y no sepas cómo, puedes buscar (Googlear) cómo hacerlo enhttpx, o incluso cómo hacerlo conrequests, dado que el diseño de HTTPX está basado en el diseño de Requests.

Luego simplemente haces lo mismo en tus pruebas.

Por ejemplo:

  • Para pasar un parámetro depath oquery, añádelo a la URL misma.
  • Para pasar un cuerpo JSON, pasa un objeto de Python (por ejemplo, undict) al parámetrojson.
  • Si necesitas enviarForm Data en lugar de JSON, usa el parámetrodata en su lugar.
  • Para pasarheaders, usa undict en el parámetroheaders.
  • Paracookies, undict en el parámetrocookies.

Para más información sobre cómo pasar datos al backend (usandohttpx o elTestClient) revisa ladocumentación de HTTPX.

Información

Ten en cuenta que elTestClient recibe datos que pueden ser convertidos a JSON, no modelos de Pydantic.

Si tienes un modelo de Pydantic en tu prueba y quieres enviar sus datos a la aplicación durante las pruebas, puedes usar eljsonable_encoder descrito enCodificador Compatible con JSON.

Ejecútalo

Después de eso, solo necesitas instalarpytest.

Asegúrate de crear unentorno virtual, activarlo y luego instalarlo, por ejemplo:

$pipinstallpytest---> 100%

Detectará los archivos y pruebas automáticamente, ejecutará las mismas y te reportará los resultados.

Ejecuta las pruebas con:

$pytest================ test session starts ================platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1rootdir: /home/user/code/superawesome-cli/appplugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1collected 6 items---> 100%test_main.py <span style="color: green; white-space: pre;">......                            [100%]</span><span style="color: green;">================= 1 passed in 0.03s =================</span>

[8]ページ先頭

©2009-2026 Movatter.jp