📌 Hızlı Özet & Temel Çıkarım:
Yazılım Test Mühendisliği, yazdığınız kodun yalnızca çalıştığını değil; beklenmedik girdilerde, ağ kopmalarında ve sınır değerlerde de öngörülebilir şekilde davrandığını matematiksel olarak kanıtlama disiplinidir. Test Piramidi uyarınca; hızlı ve ucuz çalışan binlerce Birim Test (Unit Test), servisler arası iletişimi doğrulayan Entegrasyon Testleri (Integration Tests) ve kullanıcı deneyimini test eden Uçtan Uca (E2E) testler dengeli bir orkestrasyonla kurgulanmalıdır.
1. Test Piramidi ve Kaynak Dağılımı
Ekiplerin yaptığı en yaygın hata, test piramidini ters çevirmektir (dondurma külahı anti-pattern). Bütün testleri yavaş, kırılgan ve pahalı olan Selenium veya Playwright gibi arayüz testlerine yüklemek CI/CD sürelerini saatlere uzatır.
Sağlıklı bir test piramidi:
- Birim Testleri (%70): Fonksiyon ve sınıf seviyesindedir. Veritabanına veya internete bağlanmaz; milisaniyeler içinde binlercesi biter.
- Entegrasyon Testleri (%20): Veritabanı, Redis veya mesaj kuyruğuyla (Kafka) gerçek Docker container’ları (Testcontainers) üzerinden test yapılır.
- Uçtan Uca / E2E Testleri (%10): Sistemin baştan sona canlı akışını denetler.
2. PyTest Fixture ve Mocking Uygulaması
Harici servis çağrılarını (örneğin Stripe ödeme kapısı veya hava durumu API’si) birim testlerinde gerçek sunuculara göndermek hem maliyetlidir hem de ağ dalgalanmalarında sahte kırmızı testlere (flaky tests) yol açar.
Aşağıdaki PyTest örneği, bağımlılıkların nasıl izole edildiğini ve fixture mekanizmasının gücünü gösterir:
import pytest
from unittest.mock import MagicMock
# Test edilecek is mantigi
def execute_bank_transfer(account_service, from_acc: str, to_acc: str, amount: float) -> bool:
if amount <= 0:
raise ValueError("Transfer tutari pozitif olmalidir.")
balance = account_service.get_balance(from_acc)
if balance < amount:
return False
account_service.debit(from_acc, amount)
account_service.credit(to_acc, amount)
return True
# PyTest Fixture Tanimlamasi
@pytest.fixture
def mock_account_service():
service = MagicMock()
# Varsayilan bakiye davranisini taklit et
service.get_balance.return_value = 1000.0
return service
def test_successful_transfer(mock_account_service):
result = execute_bank_transfer(mock_account_service, "ACC_1", "ACC_2", 250.0)
assert result is True
mock_account_service.debit.assert_called_once_with("ACC_1", 250.0)
mock_account_service.credit.assert_called_once_with("ACC_2", 250.0)
def test_insufficient_funds(mock_account_service):
mock_account_service.get_balance.return_value = 50.0
result = execute_bank_transfer(mock_account_service, "ACC_1", "ACC_2", 200.0)
assert result is False
mock_account_service.debit.assert_not_called()
def test_negative_amount_raises_error(mock_account_service):
with pytest.raises(ValueError, match="pozitif olmalidir"):
execute_bank_transfer(mock_account_service, "ACC_1", "ACC_2", -50.0)
Bu üç test grubu, transfer mantığının tüm kritik sınır koşullarını gerçek bir banka bağlantısına ihtiyaç duymadan 5 milisaniyede doğrular.
3. Kod Kapsama (Code Coverage) Yanılgısı
Yönetim panellerinde görünen “%100 Test Coverage” etiketi projenin hatasız olduğu anlamına gelmez. Bir test fonksiyonu çağırabilir fakat hiçbir assert kontrolü yapmayabilir; satır çalışmış görünür ama doğruluk test edilmemiştir.
Önemli olan satır sayısı değil, mantıksal dal (branch coverage) ve sınır durumlarının (edge cases - boş liste, null değer, aşırı yük) test edilmiş olmasıdır.
4. İlgili Konular ve İç Bağlantılar
- Modern CI/CD Hatlarında DevSecOps — Testlerin GitHub Actions üzerinde otomatik koşumu.
- Temiz Kod ve SOLID Prensiplerinin Mühendislik Karşılığı — Bağımlılıkların tersine çevrilmesi ile test edilebilirlik.
- Python ile Eşzamanlı Programlama — Asenkron fonksiyonların test edilmesi (
pytest-asyncio).
5. Sıkça Sorulan Sorular
Test Driven Development (TDD) her projede uygulanmalı mıdır?
TDD (Önce testi yaz, sonra kodu geliştir), gereksinimleri net olan karmaşık iş mantıklarında (finans algoritmaları, navigasyon hesaplamaları) müthiştir. Ancak hızlı prototipleme yapılan veya gereksinimlerin her saat değiştiği erken aşama Ar-Ge projelerinde geliştirme hızını yavaşlatabilir.
Mock ile Stub arasındaki fark nedir?
Stub, teste hazır sahte veri döndüren pasif bir nesnedir. Mock ise yalnızca veri dönmekle kalmaz; test bitiminde o metodun kaç kez çağrıldığını ve hangi argümanlarla çağrıldığını da doğrulayan (behavior verification) akıllı bir test çiftidir.
6. Mutasyon Testi (Mutation Testing) ile Test Kalitesinin Ölçülmesi
Kod kapsama (coverage) oranı yanıltıcı olduğunda, testlerin gerçek gücünü ölçmenin modern yolu Mutasyon Testidir (Python için mutmut).
Mutasyon aracı kaynak kodunuza kasıtlı olarak küçük yapay hatalar enjekte eder (mutantlar):
- Bir
>işaretini<yapar, - Bir
+işaretini-yapar, - Bir
TruedeğeriniFalseyapar.
Ardından test süitinizi çalıştırır. Eğer testleriniz bu değişikliği fark edip patlarsa (kırmızı olursa), mutant öldürülmüş (Killed Mutant) sayılır. Eğer testleriniz bu kasıtlı bozulmaya rağmen hala yeşil geçiyorsa, testlerinizin ilgili mantığı gerçekten doğrulamadığı kanıtlanır. Yüksek bir mutasyon skoru, sistemin gerçek anlamda kırılmaz olduğunu belgeler.
7. CI/CD Hatlarında Paralel Test Koşumu (PyTest-Xdist)
Projedeki test sayısı binleri aştığında testlerin tek tek sıralı koşulması geliştiricilerin dakikalarca beklemesine yol açar.
pytest-xdist eklentisi ile testler CPU çekirdeklerine bölünerek paralel koşturulur:
# Tum CPU cekirdeklerini kullanarak testleri 4 kat hizli bitirme
pytest -n auto --dist loadscope
Burada dikkat edilmesi gereken nokta, testlerin paylaşımlı global duruma (shared database / global variables) bağımlı olmamasıdır. Her test kendi izole fixture verisiyle çalıştığında test süresi dakikalardan saniyelere iner.
8. Sahte Veri Üretimi ve Property-Based Testing (Hypothesis)
Birim testlerinde yalnızca kendi aklınıza gelen birkaç statik örnekle (örneğin "test@example.com", 100, 0) test yapmak gizli sınır hatalarını gözden kaçırabilir.
Modern test mühendisliğinde Özellik Tabanlı Test (Property-Based Testing) yaklaşımı kullanılır (Python için hypothesis kütüphanesi).
Bu araç fonksiyonunuza binlerce rastgele, aşırı ve absürt girdi gönderir:
- Milyonlarca basamaklı sayılar,
- Boş dizgiler ve garip Unicode karakterleri (