committed by
GitHub
2 changed files with 249 additions and 0 deletions
@ -0,0 +1,118 @@ |
|||
import pytest |
|||
from fastapi import APIRouter, FastAPI, Request |
|||
from fastapi.responses import JSONResponse |
|||
from fastapi.testclient import TestClient |
|||
from pydantic import BaseModel |
|||
|
|||
# ====================== |
|||
# Configuração do app e rotas |
|||
# ====================== |
|||
|
|||
app = FastAPI() |
|||
router = APIRouter() |
|||
|
|||
|
|||
class Item(BaseModel): |
|||
nome: str |
|||
quantidade: int |
|||
|
|||
|
|||
@router.route("/items/", methods=["GET", "POST"]) |
|||
async def read_items(request: Request): |
|||
if request.method == "POST": |
|||
try: |
|||
dados = await request.json() |
|||
item = Item(**dados) |
|||
return JSONResponse( |
|||
{"message": "Item criado", "item": item.model_dump()}, status_code=201 |
|||
) |
|||
except Exception: |
|||
return JSONResponse({"detail": "Erro ao processar JSON"}, status_code=400) |
|||
return JSONResponse({"hello": "world"}) |
|||
|
|||
|
|||
app.include_router(router) |
|||
client = TestClient(app) |
|||
|
|||
|
|||
################ Testes |
|||
|
|||
|
|||
# Testa se a rota GET /items/ retorna a resposta padrão esperada |
|||
def test_get_items(): |
|||
resposta = client.get("/items/") |
|||
assert resposta.status_code == 200 |
|||
assert resposta.json() == {"hello": "world"} |
|||
|
|||
|
|||
# Testa se a rota POST /items/ aceita e retorna corretamente um JSON válido |
|||
def test_post_items(): |
|||
payload = {"nome": "Caderno", "quantidade": 10} |
|||
resposta = client.post("/items/", json=payload) |
|||
assert resposta.status_code == 201 |
|||
assert resposta.json() == { |
|||
"message": "Item criado", |
|||
"item": {"nome": "Caderno", "quantidade": 10}, |
|||
} |
|||
|
|||
|
|||
# Testa se a rota POST /items/ retorna erro ao receber JSON inválido (faltando campo obrigatório) |
|||
def test_post_json_invalido(): |
|||
payload = {"quantidade": 5} # Campo 'nome' está ausente |
|||
resposta = client.post("/items/", json=payload) |
|||
assert resposta.status_code == 400 |
|||
assert resposta.json()["detail"] == "Erro ao processar JSON" |
|||
|
|||
|
|||
# Testa se a rota POST /items/ retorna erro ao receber corpo que não seja JSON |
|||
def test_post_com_corpo_nao_json(): |
|||
resposta = client.post("/items/", content="texto") # Texto cru, não é JSON |
|||
assert resposta.status_code == 400 |
|||
assert resposta.json()["detail"] == "Erro ao processar JSON" |
|||
|
|||
|
|||
# Testa se a rota POST /items/ aceita headers customizados e responde corretamente |
|||
def test_post_com_header_customizado(): |
|||
headers = {"Custom-Header": "123"} |
|||
payload = {"nome": "Caneta", "quantidade": 3} |
|||
resposta = client.post("/items/", json=payload, headers=headers) |
|||
assert resposta.status_code == 201 |
|||
assert resposta.json()["item"]["nome"] == "Caneta" |
|||
|
|||
|
|||
# Testa se métodos não permitidos como PUT são corretamente bloqueados pela API |
|||
def test_method_not_allowed(): |
|||
resposta = client.put("/items/") |
|||
assert resposta.status_code == 405 # 405 Method Not Allowed |
|||
assert "detail" in resposta.json() |
|||
|
|||
|
|||
# Testa múltiplos métodos HTTP para a mesma rota usando parametrização |
|||
# Verifica se cada método responde com o status esperado |
|||
@pytest.mark.parametrize( |
|||
"metodo,status_esperado", |
|||
[ |
|||
("GET", 200), |
|||
("POST", 201), |
|||
("PUT", 405), |
|||
("DELETE", 405), |
|||
("PATCH", 405), |
|||
], |
|||
) |
|||
def test_varios_metodos(metodo, status_esperado): |
|||
payload = {"nome": "Caneta", "quantidade": 1} |
|||
resposta = client.request(metodo, "/items/", json=payload) |
|||
assert resposta.status_code == status_esperado |
|||
|
|||
|
|||
# Testa se a resposta da rota GET /items/ contém o header correto de Content-Type (application/json) |
|||
def test_headers_da_resposta(): |
|||
resposta = client.get("/items/") |
|||
assert resposta.headers["content-type"].startswith("application/json") |
|||
|
|||
|
|||
# Testa se a rota GET ignora parâmetros de query (ex: ?busca=algo) |
|||
def test_get_com_query_params_ignorados(): |
|||
resposta = client.get("/items/?busca=algo") |
|||
assert resposta.status_code == 200 |
|||
assert resposta.json() == {"hello": "world"} |
@ -0,0 +1,131 @@ |
|||
# https://github.com/fastapi/fastapi/issues/10180 |
|||
|
|||
from fastapi import APIRouter, FastAPI |
|||
from fastapi.testclient import TestClient |
|||
|
|||
|
|||
def test_mount_subapp_on_apirouter_should_not_work(): |
|||
app = FastAPI() |
|||
router = APIRouter(prefix="/api") |
|||
|
|||
@router.get("/main") |
|||
def main(): |
|||
return {"msg": "main"} |
|||
|
|||
subapp = FastAPI() |
|||
|
|||
@subapp.get("/sub") |
|||
def sub(): |
|||
return {"msg": "sub"} |
|||
|
|||
# Tentativa de montar subapp no router (não funciona) |
|||
router.mount("/subapi", subapp) |
|||
app.include_router(router) |
|||
|
|||
client = TestClient(app) |
|||
# A rota principal funciona |
|||
assert client.get("/api/main").status_code == 200 |
|||
# A rota da subaplicação NÃO funciona (deveria retornar 404) |
|||
assert client.get("/api/subapi/sub").status_code == 404 |
|||
|
|||
|
|||
def test_mount_subapp_on_app_should_work(): |
|||
app = FastAPI() |
|||
router = APIRouter(prefix="/api") |
|||
|
|||
@router.get("/main") |
|||
def main(): |
|||
return {"msg": "main"} |
|||
|
|||
subapp = FastAPI() |
|||
|
|||
@subapp.get("/sub") |
|||
def sub(): |
|||
return {"msg": "sub"} |
|||
|
|||
app.include_router(router) |
|||
# Montando corretamente no app principal |
|||
app.mount("/api/subapi", subapp) |
|||
|
|||
client = TestClient(app) |
|||
# A rota principal funciona |
|||
assert client.get("/api/main").status_code == 200 |
|||
# A rota da subaplicação funciona |
|||
assert client.get("/api/subapi/sub").status_code == 200 |
|||
|
|||
|
|||
# Testes adicionais para melhorar cobertura: |
|||
|
|||
|
|||
def test_mount_multiple_subapps(): |
|||
"""Testa se é possível montar múltiplas subaplicações""" |
|||
app = FastAPI() |
|||
|
|||
# Primeira subaplicação |
|||
subapp1 = FastAPI() |
|||
|
|||
@subapp1.get("/test1") |
|||
def read_test1(): |
|||
return {"msg": "test1"} |
|||
|
|||
# Segunda subaplicação |
|||
subapp2 = FastAPI() |
|||
|
|||
@subapp2.get("/test2") |
|||
def read_test2(): |
|||
return {"msg": "test2"} |
|||
|
|||
# Montando ambas as subaplicações |
|||
app.mount("/sub1", subapp1) |
|||
app.mount("/sub2", subapp2) |
|||
|
|||
client = TestClient(app) |
|||
# Verifica se ambas as subaplicações funcionam |
|||
assert client.get("/sub1/test1").status_code == 200 |
|||
assert client.get("/sub2/test2").status_code == 200 |
|||
assert client.get("/sub1/test1").json() == {"msg": "test1"} |
|||
assert client.get("/sub2/test2").json() == {"msg": "test2"} |
|||
|
|||
|
|||
def test_nested_routes(): |
|||
"""Testa rotas aninhadas em diferentes níveis""" |
|||
app = FastAPI() |
|||
|
|||
# Criando routers aninhados |
|||
router1 = APIRouter(prefix="/v1") |
|||
router2 = APIRouter(prefix="/api") |
|||
|
|||
@router2.get("/deep") |
|||
def read_deep(): |
|||
return {"msg": "deep route"} |
|||
|
|||
# Aninhando os routers |
|||
router1.include_router(router2) |
|||
app.include_router(router1) |
|||
|
|||
client = TestClient(app) |
|||
# Verifica se a rota aninhada funciona |
|||
response = client.get("/v1/api/deep") |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"msg": "deep route"} |
|||
|
|||
# Verifica se uma rota inexistente retorna 404 |
|||
assert client.get("/v1/api/nonexistent").status_code == 404 |
|||
|
|||
|
|||
def test_method_not_allowed(): |
|||
"""Testa se métodos HTTP não permitidos são tratados corretamente""" |
|||
app = FastAPI() |
|||
|
|||
@app.get("/only-get") |
|||
def read_only(): |
|||
return {"msg": "get only"} |
|||
|
|||
client = TestClient(app) |
|||
# Verifica se GET funciona |
|||
assert client.get("/only-get").status_code == 200 |
|||
|
|||
# Verifica se outros métodos retornam 405 (Method Not Allowed) |
|||
assert client.post("/only-get").status_code == 405 |
|||
assert client.put("/only-get").status_code == 405 |
|||
assert client.delete("/only-get").status_code == 405 |
Loading…
Reference in new issue