Giriş
pytest, Python ekosisteminin en yaygın test framework'üdür; minimal boilerplate, zengin fixture sistemi ve geniş plugin ekosistemi sunar. Birim testlerin ötesinde entegrasyon testleri, modüllerin gerçek bağımlılıklarla birlikte doğru çalıştığını doğrular.
Bu yazıda fixture scope'ları, conftest.py organizasyonu, unittest.mock ve pytest-mock ile mock'lama, veritabanı ve HTTP istemcisi entegrasyonu ve CI'da test izolasyonunu ele alıyoruz. Flaky testleri azaltmak için deterministik veri ve transaction rollback desenlerine odaklanıyoruz.
pytest Temelleri ve Fixture'lar
pytest test fonksiyonlarını otomatik keşfeder; assert ifadeleri detaylı karşılaştırma çıktısı verir. @pytest.fixture ile paylaşılan kurulum ve söküm tanımlanır. scope parametresi (function, class, module, session) fixture'ın ne sıklıkla yeniden oluşturulacağını belirler.
yield fixture'lar test sonrası temizlik için idealdir: veritabanı bağlantısı açılır, test çalışır, yield sonrası rollback veya kapatma yapılır. autouse=True ile her teste otomatik enjekte edilen fixture'lar ortak konfigürasyon için kullanılır.
import pytest
@pytest.fixture
def db_session():
session = create_session()
yield session
session.rollback()
session.close()
def test_create_user(db_session):
user = User(name="Ali")
db_session.add(user)
db_session.commit()
assert user.id is not Noneconftest.py ve Test Organizasyonu
conftest.py dosyaları fixture'ları test dizinine göre hiyerarşik paylaşır; tests/conftest.py tüm alt testlerde geçerlidir. tests/integration/ altında ağır entegrasyon testlerini ayırmak, pytest -m 'not integration' ile hızlı birim test döngüsü sağlar.
@pytest.mark.parametrize ile aynı testi çoklu girdiyle çalıştırmak kod tekrarını azaltır. pytest.ini veya pyproject.toml içinde markers, testpaths ve addopts tanımlanır.
- tests/unit/: hızlı birim testler
- tests/integration/: DB ve API testleri
- conftest.py: paylaşılan fixture'lar
- markers: integration, slow, smoke
Mocking ve monkeypatch
unittest.mock.patch veya pytest-mock'un mocker fixture'ı dış servisleri izole eder. @patch('mymodule.requests.get') ile HTTP çağrısı sahte yanıt döndürür. MagicMock ile dönüş değeri ve yan etki davranışı tanımlanır.
pytest monkeypatch ortam değişkenlerini ve nesne özniteliklerini geçici değiştirir; test bitince otomatik geri alınır. Entegrasyon testlerinde mock'u minimumda tutun; yalnızca kontrol dışı dış sistemleri (ödeme gateway, e-posta) mock'layın, kendi veritabanınızı gerçek kullanın.
def test_fetch_user(mocker):
mock_get = mocker.patch("app.client.requests.get")
mock_get.return_value.json.return_value = {"id": 1, "name": "Ali"}
result = fetch_user(1)
assert result["name"] == "Ali"
mock_get.assert_called_once_with("/users/1")Veritabanı Entegrasyon Testleri
Test veritabanı üretimden ayrı olmalıdır; Docker Compose ile postgres:16 servisi CI'da kaldırılır. SQLAlchemy'de transaction fixture: her test bir transaction içinde çalışır, sonunda rollback edilir; veri birikimi olmaz.
pytest-django ve pytest-asyncio framework'e özel entegrasyon sağlar. Factory Boy veya Faker ile test verisi üretin; sabit ID'lere bağımlı testler kırılgandır. Alembic migration'ları test ortamında da çalıştırılarak şema uyumu doğrulanır.
API ve HTTP Entegrasyon Testleri
FastAPI TestClient veya httpx.AsyncClient ile uygulama sunucusu açmadan endpoint test edilir. Starlette'nin TestClient'ı ASGI uygulamasını doğrudan çağırır. Gerçek TCP bağlantısı gerekiyorsa pytest ile subprocess'te uvicorn başlatılabilir.
responses veya httpx mock transport ile dış API bağımlılıkları simüle edilir. Contract test (Pact) ile tüketici ve sağlayıcı API sözleşmesi doğrulanır; mikroservis mimarilerinde regresyonu erken yakalar.
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_health():
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}CI, Coverage ve Flaky Testler
pytest --cov=app --cov-report=xml coverage raporu üretir; %100 hedefi yerine kritik yolları kapsamak daha gerçekçidir. pytest-xdist ile paralel test (-n auto) süreyi kısaltır; paylaşılan fixture'ların scope'una dikkat edin.
Flaky testler genelde zaman, sıra veya paylaşılan durum kaynaklıdır. pytest-rerunfailures geçici hatalarda yeniden dener; kök nedeni çözmeden kalıcı çözüm değildir. Testler arası izolasyon ve sabit seed (random.seed) kullanın.
- Her test bağımsız ve tekrarlanabilir olsun
- Entegrasyon testlerini marker ile ayırın
- CI'da gerçek servisler veya testcontainers kullanın
- Flaky testleri izole edip düzeltin, bastırmayın
Sonuç
pytest ile iyi organize edilmiş entegrasyon testleri, refactor güvenini ve deploy hızını artırır. Fixture hiyerarşisi, doğru mock sınırları ve izole test veritabanı üçlüsü sürdürülebilir bir test süitinin temelidir.
Test piramidini koruyun: çoğunluk hızlı birim test, az sayıda kritik entegrasyon testi. Yeni özellik eklerken önce başarısız test yazmak (TDD) uzun vadede regresyon maliyetini düşürür.