📌 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:

  1. Birim Testleri (%70): Fonksiyon ve sınıf seviyesindedir. Veritabanına veya internete bağlanmaz; milisaniyeler içinde binlercesi biter.
  2. Entegrasyon Testleri (%20): Veritabanı, Redis veya mesaj kuyruğuyla (Kafka) gerçek Docker container’ları (Testcontainers) üzerinden test yapılır.
  3. 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


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 True değerini False yapar.

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 (, emoji dizileri),
  • Aşırı büyük negatif değerler.

Eğer fonksiyonunuz bu rastgele girdilerin herhangi birinde beklenmedik bir istisna fırlatırsa, kütüphane hatayı tetikleyen en küçük girdiyi otomatik olarak bularak önünüze serer. Bu yöntem, özellikle finansal algoritmalar ve güvenlik protokolleri için en yüksek test güvencesidir.