committed by
GitHub
2 changed files with 243 additions and 0 deletions
@ -0,0 +1,113 @@ |
|||||
|
from fastapi import APIRouter, FastAPI, Request |
||||
|
from fastapi.responses import JSONResponse |
||||
|
from fastapi.testclient import TestClient |
||||
|
from pydantic import BaseModel |
||||
|
import pytest |
||||
|
|
||||
|
# ====================== |
||||
|
# 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,130 @@ |
|||||
|
# https://github.com/fastapi/fastapi/issues/10180 |
||||
|
|
||||
|
from fastapi import FastAPI, APIRouter |
||||
|
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