Yükleniyor...

FastAPI Testing Rehberi: Profesyonel Test Stratejileri

Yazar: Burak Balkı | Kategori: Testing | Okuma Süresi: 9 dk

FastAPI uygulamaları için kapsamlı test stratejileri rehberi. Pytest, TestClient, asenkron testler, dependency injection mocking ve CI/CD entegrasyonu hakkın...

## FastAPI Testing Temelleri: Neden Test Yapmalıyız? Modern yazılım geliştirme süreçlerinde, özellikle **FastAPI** gibi yüksek performanslı frameworkler kullanırken, kodun doğruluğunu garanti altına almak kritik öneme sahiptir. **FastAPI testing** süreçleri, uygulamanızın son kullanıcıya ulaşmadan önce mantıksal hatalardan arındırılmasını sağlar. Test tabanlı geliştirme (TDD), sadece hataları bulmakla kalmaz, aynı zamanda kodun dokümantasyonuna ve tasarım kalitesine de katkıda bulunur. FastAPI, yapısı gereği **Starlette** üzerine inşa edilmiştir ve test işlemleri için doğrudan Starlette'in `TestClient` sınıfını kullanır. Bu rehberde, basit bir API'dan karmaşık asenkron işlemlere kadar tüm test senaryolarını ele alacağız. ## Test Ortamının Kurulumu: Pytest ve HTTPX FastAPI projelerinde standart test aracı **Pytest**'tir. Pytest'in esnek yapısı ve geniş eklenti ekosistemi, API testlerini oldukça kolaylaştırır. Ayrıca asenkron testler için `httpx` kütüphanesine ihtiyaç duyacağız. Öncelikle gerekli kütüphaneleri yükleyelim: ```bash pip install pytest httpx fastapi uvicorn ``` Proje yapınızı şu şekilde organize etmeniz önerilir: ```text . ├── app/ │ ├── main.py │ └── dependencies.py └── tests/ ├── __init__.py └── test_main.py ``` ## İlk Testin Yazılması: TestClient Kullanımı `TestClient`, FastAPI uygulamanıza gerçek bir HTTP sunucusu ayağa kaldırmadan istek atmanızı sağlar. Bu, testlerin milisaniyeler içinde tamamlanmasına olanak tanır. ### Uygulama Kodu (main.py) ```python from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"message": "Merhaba Dünya"} ``` ### Test Kodu (test_main.py) ```python from fastapi.testclient import TestClient from app.main import app client = TestClient(app) def test_read_main(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"message": "Merhaba Dünya"} ``` > **Önemli Not:** `TestClient` senkron bir yapıdır. Eğer uygulamanızda sadece `def` ile tanımlanmış endpointler varsa bu yeterlidir. Ancak `async def` kullanıyorsanız asenkron test stratejilerini uygulamanız gerekir. ## FastAPI Dependency Injection ve Mocking FastAPI'ın en güçlü özelliklerinden biri olan **Dependency Injection** (Bağımlılık Enjeksiyonu), test sırasında bileşenleri taklit etmeyi (mocking) son derece kolaylaştırır. Örneğin, bir veritabanı bağlantısını veya dış servis çağrısını test sırasında sahte bir nesneyle değiştirebilirsiniz. ```python from fastapi import Depends, FastAPI from app.dependencies import get_query_token app = FastAPI() @app.get("/items/") async def read_items(token: str = Depends(get_query_token)): return {"token": token} # Test dosyasında bağımlılığı ezme from app.main import app def override_get_query_token(): return "fake-token" app.dependency_overrides[get_query_token] = override_get_query_token def test_read_items_with_override(): response = client.get("/items/") assert response.status_code == 200 assert response.json() == {"token": "fake-token"} ``` ## Asenkron (Async) Test Stratejileri FastAPI asenkron bir framework olduğu için, bazı durumlarda testlerinizin de asenkron olması gerekir. Bunun için `pytest-asyncio` ve `httpx.AsyncClient` kullanmalıyız. ```python import pytest from httpx import AsyncClient from app.main import app @pytest.mark.asyncio async def test_async_endpoint(): async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.get("/") assert response.status_code == 200 ``` ## Veritabanı Testleri: Test Veritabanı Yönetimi Veritabanı testlerinde en önemli kural, testlerin prodüksiyon verilerine zarar vermemesi ve her testten sonra veritabanının temizlenmesidir. Genellikle bellek içi (in-memory) **SQLite** kullanılır. | Veritabanı Tipi | Kullanım Senaryosu | Avantajı | | :--- | :--- | :--- | | SQLite (In-memory) | Birim Testler | Çok hızlı, kurulum gerektirmez | | PostgreSQL (Docker) | Entegrasyon Testleri | Gerçek dünya senaryosu sunar | | Mock DB | Hızlı Mantık Testleri | I/O gerektirmez | ### Veritabanı Fixture Örneği ```python import pytest from sqlalchemy import create_mock_engine @pytest.fixture(name="session") def session_fixture(): # Veritabanı oluşturma ve session başlatma işlemleri db = TestingSessionLocal() try: yield db finally: db.close() ``` ## Pytest Fixtures ile Kod Tekrarını Önleme Fixtures, testler arasında paylaşılan kurulum ve temizleme kodlarını yönetmek için kullanılır. `conftest.py` dosyası içinde tanımlanan fixture'lar tüm test dosyaları tarafından erişilebilir. ```python import pytest from fastapi.testclient import TestClient from app.main import app @pytest.fixture def client(): with TestClient(app) as c: yield c def test_with_fixture(client): response = client.get("/") assert response.status_code == 200 ``` ## Middleware ve Exception Handler Testleri Custom middleware veya hata yakalayıcıların (exception handlers) doğru çalışıp çalışmadığını test etmek, uygulama güvenliği için kritiktir. ```python def test_custom_exception_handler(client): response = client.get("/force-error") assert response.status_code == 418 assert response.json() == {"detail": "Çaydanlık Hatası"} ``` ## Kimlik Doğrulama (Auth) ve Güvenlik Testleri JWT tabanlı kimlik doğrulama sistemlerinde, testlerinize `Authorization` header'ı eklemeniz gerekir. ```python def test_protected_route_without_token(client): response = client.get("/users/me") assert response.status_code == 401 def test_protected_route_with_token(client): token = "valid-jwt-token" response = client.get("/users/me", headers={"Authorization": f"Bearer {token}"}) assert response.status_code == 200 ``` ## Test Coverage ve Raporlama Kodunuzun ne kadarının test edildiğini ölçmek için `pytest-cov` kullanabilirsiniz. Bu, test edilmemiş kritik noktaları belirlemenize yardımcı olur. ```bash pytest --cov=app tests/ ``` Bu komut size her dosya için kapsama oranını (coverage percentage) verecektir. Profesyonel projelerde bu oranın %80 üzerinde olması beklenir. ## CI/CD Entegrasyonu: GitHub Actions Örneği Testlerin her commit sonrasında otomatik çalışması için bir CI (Continuous Integration) pipeline'ı kurmak şarttır. Aşağıda basit bir GitHub Actions yapılandırması bulunmaktadır: ```yaml name: FastAPI Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: {python-version: '3.9'} - name: Install dependencies run: pip install -r requirements.txt - name: Run tests run: pytest ``` ## FastAPI Testing Best Practices 1. **Bağımsız Testler:** Her test kendi başına çalışabilmeli ve diğer testlerin sonucuna güvenmemelidir. 2. **Gerçekçi Veriler:** Test verileriniz (mock data) gerçek dünya senaryolarını yansıtmalıdır. 3. **Hızlı Geri Bildirim:** Testler hızlı çalışmalıdır. Yavaş testler geliştirme sürecini aksatır. 4. **Parametrize Testler:** Aynı mantığı farklı girdilerle test etmek için `pytest.mark.parametrize` kullanın. ```python @pytest.mark.parametrize("item_id, expected_status", [(1, 200), (-1, 404), (999, 404)]) def test_read_item(client, item_id, expected_status): response = client.get(f"/items/{item_id}") assert response.status_code == expected_status ``` ## Sık Yapılan Hatalar - **Production DB Kullanımı:** Asla gerçek veritabanı üzerinde test yapmayın. - **Dependency Overrides Unutulması:** Mock yapılması gereken servislerin (örn: Email servisi) gerçekte çalışması. - **Async/Await Karmaşası:** Asenkron fonksiyonları senkron `TestClient` ile çağırmaya çalışmak. - **Global State Kirliliği:** Bir testin veritabanında bıraktığı verinin bir sonraki testi etkilemesi. ## Performans İpuçları - **Parallel Testing:** `pytest-xdist` kullanarak testleri birden fazla CPU çekirdeğinde paralel çalıştırın. - **Fixture Scoping:** Eğer bir fixture her test için yeniden oluşturulmak zorunda değilse, `scope="module"` veya `scope="session"` kullanın. ## Özet ve Sonuç FastAPI uygulamalarında test yazmak, projenin sürdürülebilirliği ve güvenilirliği için opsiyonel değil, zorunludur. `TestClient` ve `Pytest` kombinasyonu, Python ekosistemindeki en güçlü test araçlarını sunar. Bu rehberdeki adımları takip ederek, uygulamanızın kalitesini artırabilir ve hata payınızı minimize edebilirsiniz.