Skip to content

Cum să testezi un API Python cu pytest și requests

DodaTech Updated 2025-01-15 3 min read

In this tutorial, you'll learn about Cum să testezi un API Python cu pytest și requests. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Testarea API-ului asigură că endpoint-urile funcționează corect, gestionează erorile și returnează date în formatul așteptat. În acest ghid vei învăța cum să testezi un API Python cu pytest, requests și TestClient.

Problema

După fiecare modificare, trebuie să testezi manual endpoint-urile. Descoperi bug-uri abia în producție, iar refactorizările sunt riscante pentru că nu ai o plasă de siguranță.

Soluția Rapidă

1. Setup pentru teste

pip install pytest httpx requests

2. Testare cu TestClient (FastAPI)

from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_lista_produse():
    response = client.get("/produse")
    assert response.status_code == 200
    assert isinstance(response.json(), list)

def test_creeaza_produs():
    produs = {"nume": "Laptop", "pret": 3500}
    response = client.post(
        "/produse",
        json=produs
    )
    assert response.status_code == 201
    data = response.json()
    assert data["nume"] == "Laptop"

def test_produs_negasit():
    response = client.get("/produse/999")
    assert response.status_code == 404
    assert "eroare" in response.json()

def test_date_invalide():
    response = client.post(
        "/produse",
        json={"nume": "", "pret": -10}
    )
    assert response.status_code == 422

3. Testare cu requests (Flask)

import requests

BASE_URL = "http://localhost:5000"

def test_lista_produse():
    r = requests.get(f"{BASE_URL}/api/produse")
    assert r.status_code == 200
    assert len(r.json()) > 0

def test_creare_produs():
    payload = {"nume": "Mouse", "pret": 150}
    r = requests.post(
        f"{BASE_URL}/api/produse",
        json=payload
    )
    assert r.status_code == 201
    assert r.json()["nume"] == "Mouse"

4. Testare cu pytest parametrizat

import pytest

@pytest.mark.parametrize("endpoint,expected_status", [
    ("/produse", 200),
    ("/produse/1", 200),
    ("/produse/999", 404),
    ("/utilizatori", 401),  # endpoint protejat
])
def test_status_code(endpoint, expected_status):
    response = client.get(endpoint)
    assert response.status_code == expected_status

@pytest.mark.parametrize("payload,expected_status", [
    ({"nume": "Laptop", "pret": 3500}, 201),
    ({"nume": ""}, 422),
    ({"pret": "nu-e-numar"}, 422),
    ({}, 422),
])
def test_creare_produs_validare(payload, expected_status):
    response = client.post("/produse", json=payload)
    assert response.status_code == expected_status

5. Testare autentificare

def test_acces_fara_token():
    response = client.get("/admin")
    assert response.status_code == 401

def test_acces_cu_token():
    # obține token
    login_r = client.post(
        "/login",
        data={"username": "admin", "password": "parola"}
    )
    token = login_r.json()["access_token"]

    # folosește token-ul
    response = client.get(
        "/admin",
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 200

6. Fixtură pentru setup

@pytest.fixture
def date_test():
    """Populează baza de date cu date de test"""
    client.post("/produse", json={"nume": "Laptop", "pret": 3500})
    client.post("/produse", json={"nume": "Mouse", "pret": 150})
    yield
    client.delete("/test/reset")  # curăță după test

def test_cauta_produs(date_test):
    response = client.get("/produse?cauta=laptop")
    assert len(response.json()) > 0

7. Test în bandă (CI/CD)

Creează fișierul .github/workflows/test.yml:

name: Testare API
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: pip install -r requirements.txt
      - run: pytest --cov=app tests/

Rulează toate testele:

pytest tests/ -v --cov=app

Output:

tests/test_api.py::test_lista_produse PASSED
tests/test_api.py::test_creeaza_produs PASSED
tests/test_api.py::test_produs_negasit PASSED
tests/test_api.py::test_date_invalide PASSED

Prevenție

  • Rulează testele înainte de fiecare commit
  • Menține o acoperire a codului de minim 80%
  • Testează atât scenariile de succes, cât și pe cele de eroare
  • Folosește o bază de date de test separată
  • Rulează testele în pipeline-ul CI/CD

Greșeli Comune

  1. Testarea doar a căii fericite -- nu testa și scenariile de eroare (404, 422, 500)
  2. Teste care depind de ordinea de execuție -- fiecare test trebuie să fie independent
  3. Ignorarea timeout-urilor -- testele pentru endpoint-uri lente trebuie să aibă timeout configurat
  4. Teste cu date hardcodate -- folosește fixturi și date dinamice
  5. Fără curățare după teste -- datele de test poluează baza de date

Exercițiu Practic

Scrie un set complet de teste pentru un API CRUD de comenzi: testează crearea, listarea, actualizarea, ștergerea, validarea datelor și autentificarea. Include test parametrizat pentru cel puțin 5 cazuri de eroare.

FAQ

### De ce să folosesc TestClient și nu requests?

TestClient (din FastAPI/Starlette) nu necesită un server rulând -- face request-uri direct în cod. Este mai rapid și nu depinde de un port disponibil.

Ce este pytest?

pytest este cel mai popular framework de testare în Python. Oferă assert-uri simple, fixturi, parametrizare și plugin-uri pentru acoperire cod, mock-uri și multe altele.

Cum testez un endpoint care depinde de baza de date?

Folosește o bază de date de test (SQLite în memorie) și fixturi pytest pentru a popula și curăța datele înainte și după fiecare test.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro