---
title: 'Test Çerçeveleri'
source: 'https://academia.sh/tr/kurslar/python-projeleri/test-cerceveleri'
course: 'Python Projeleri: Paketleme ve Test'
language: tr
updated: '2026-08-17T18:10:32+00:00'
license: 'CC BY-SA 4.0'
---

# Test Çerçeveleri

Çerçeve dört iş yapar ve bunlardan yalnız biri kararı üretir: aynı üç durum iki yazım biçiminde 1 ayrı sonuç verirken, aynı dosya üç keşif kuralında bulunan test bakımından 3 ayrı sonuç verir ve rapor üçünde de aynı kalır.

Önceki ders ortamı ve bağımlılığı tekleştirdi: yorumlayıcı sürümü sabitlendi, kilit
dosyası çözümlemenin kararını yazıya geçirdi ve aynı bildirim artık tek bir sürüm kümesi
veriyor. Kurulum tarafında sorulacak soru kalmadı. Ama kurulumun tekleşmesi, o kurulumun
üzerinde koşan kodun da her seferinde aynı kararı verdiği anlamına gelmiyor.

Bu dersin sorusu şudur: **kodun kendisi her koşumda aynı sonucu mu veriyor, ve bunu bize
kim söylüyor?** Söyleyen şey bir **test çerçevesidir (test framework)**. Çerçeve bir ürün
değil, bir **araç sınıfıdır**; bu derste hiçbir ürün adı geçmez. Çerçevenin yaptığı işler
ders içinde modellenir, standart kitaplığın sunduğu çerçeve ise doğrudan kullanılır.

## Çerçevenin Dört İşi

Bir test çerçevesi tek bir iş yapıyormuş gibi görünür — "testleri koşturur" — ama aslında
dört ayrı iş yapar ve bunların yalnız biri sonucu belirler.

**Keşif.** Hangi çağrılabilirlerin test olduğuna karar verir. Bu karar bir adlandırma
kuralına, bir dizin desenine ya da bir işaretlemeye dayanır ve **testin içeriğine
bakmaz**. Kuralın kaynağı çerçevenin varsayılanı olabilir, projenin ayarı da olabilir;
her iki durumda da kural testin dışındadır.

**Koşum.** Keşfedilen her testi çağırır, sırayı belirler, gerekirse hazırlık ve toplama
adımlarını araya yerleştirir. Sıranın kendisi de bir karardır ve sonraki dersin ölçtüğü
şeydir.

**İddia.** Testin içinde beklenen ile gerçekleşeni karşılaştırır. Python'da bunun en yalın
biçimi `assert` deyimidir; deyim yanlışlanınca `AssertionError` fırlatır ve çerçeve bu
istisnayı düşen test olarak sayar. İstisnanın akış denetimi olarak kullanılması Python
Temelleri kursunda kurulmuştu; burada aynı düzenek raporlama için kullanılıyor.

**Rapor.** Kaç testin koştuğunu, kaçının geçtiğini ve düşenlerin adını yazar; okuyan kişi
kararı bu satırlardan alır.

Bu dört işten yalnız **iddia** doğruluk hakkında bir şey söyler. Keşif, koşum ve rapor
yapılandırma kararlarıdır — ve bu dersin ölçtüğü şey, yapılandırma kararlarının sonuca ne
yaptığıdır.

Dördü de bir arada durduğu için tek bir iş gibi görünürler. Oysa yalnız iddia projenin
kaynak dosyalarında yazılıdır; keşif kuralı ve koşum düzeni çoğu kurulumda ayrı bir
yapılandırma dosyasında ya da koşturma komutunun seçeneklerinde durur. Yani kararın bir
parçası kodun içinde, bir parçası dışındadır — ve dışarıda duran parça değiştiğinde kod
dosyalarında hiçbir iz kalmaz. Bu kursun sorusu bakımından bu ayrım tam olarak aranan
şeydir: aynı kaynak, ayrı yapılandırma, kaç ayrı sonuç.

## İddianın Kâhine İhtiyacı

Bir iddia iki şeyi karşılaştırır: kodun ürettiği değer ile **beklenen** değer. Beklenen
değerin nereden geldiği testin kendi içinde görünmez; onu yazan kişi bir yerden almıştır.
Bu kaynağa **kâhin (oracle)** denir ve kavram Yazılım Kalitesi ve Test müfredatının Kalite
ve Test Temelleri kursunda kurulmuştur. Burada kuram tekrarlanmıyor; kâhinin **ölçüme ne
kattığı** ölçülüyor.

Ortak kurgumuzda kâhin bizizdir: doğru indirim davranışını, temiz ortamı ve doğru sürüm
kümesini biz yazdık. Bu dersin test durumları ise kâhinden değil, **işlevin gözlenen
davranışından** yazılmıştır. Yani beklenen değerler, işlev bugün ne veriyorsa o yazılarak
elde edildi. Böyle yazılmış bir test, işlevin davranışının **değişmediğini** doğrular;
davranışın **doğru olduğunu** doğrulamaz. Aradaki farkın ne ödettiği bu konunun son
dersinde ölçülecek; şimdilik şunu saptamak yeterli: **bir testin geçmesi, beklenen değerin
doğru olduğunu söylemez.**

İddianın yazılışı da bir seçimdir. `assert` deyimi bir tümce yanlışlanınca `AssertionError`
fırlatır ve raporda yalnız "bu satırdaki deyim yanlışlandı" bilgisi kalır. Çerçevenin
karşılaştırma yöntemleri ise iki değeri de yakalar ve rapora yazar. Bu fark tanı süresini
kısaltır ama kararı değiştirmez; ölçümün üst tablosu tam olarak bunu gösteriyor.

## İddiasız Test Sessizce Geçer

Modellenen koşucunun kuralı basit bir kuraldır: testi çağır, `AssertionError` gelmezse
geçmiş say. Bu kuralın doğrudan bir sonucu vardır — **hiçbir iddia içermeyen bir test her
zaman geçer.** Koşucu iddiayı saymaz, istisnayı sayar; iddia hiç yazılmamışsa fırlatılacak
istisna da yoktur.

Bu, çerçevenin bir kusuru değil, sınırıdır. Çerçeve testin gövdesine bakmaz; baksaydı,
hangi çağrının iddia sayılacağını bilmesi gerekirdi ve bu bilgi uygulamaya özgüdür.
Ölçümdeki `yardimci_tutar` bu sınırın somut örneğidir: bir iddia taşımaz, dolayısıyla
keşfedilseydi de geçmiş sayılırdı. Aynı şey, iddiası yanlışlıkla yorum satırına alınmış ya
da bir koşulun içinde kalıp hiç değerlendirilmeyen bir test için de geçerlidir.

Sonuç şudur: **geçen bir test, en az iki ayrı nedenden geçmiş olabilir** — iddia doğrulandı
ya da hiç iddia yoktu. Rapor bu ikisini ayırmaz. Ayrım için bakılacak yer rapor değil,
testin kendisidir; ve bunu kodun üzerinden ölçen düzenek bu konunun son dersinde kurulacak.

## Aynı Üç Durum, İki Yazım Biçimi

Birim testin iki yaygın yazım biçimi vardır. Birincisinde her test bir işlevdir ve iddia
`assert` deyimiyle yazılır; koşucu işlevi çağırır, istisna yakalarsa testi düşmüş sayar.
İkincisinde testler bir sınıfın yöntemleridir ve iddia, çerçevenin sunduğu karşılaştırma
yöntemleriyle yazılır; standart kitaplığın `unittest` çerçevesi bu biçimi kullanır.

Aradaki fark bir **yazım** farkıdır. İkinci biçim düşen bir iddiada iki değeri de rapora
yazar, birincisi yalnız deyimin yanlışlandığını söyler. Bu bir rapor ayrıntısıdır ve
kararın kendisini değiştirmez — ölçüm bunu gösteriyor.

Sınıf tabanlı biçimin ikinci bir katkısı vardır: her testin öncesinde ve sonrasında
çalışan kancalar tanımlanabilir. Bu kancalar tekrarı testten çıkarmanın bir yoludur ve
sonraki dersin konusudur; burada yalnız üç yalın test yazıldığı için kanca kullanılmıyor
ve iki biçim gerçekten aynı işi yapıyor. Karşılaştırmanın anlamlı olması için de bu
gerekiyordu: iki biçim ancak aynı üç durumu aynı hazırlıkla koşturduğunda yan yana konabilir.

## Keşif Kuralı Neyi Belirler

Keşif kuralı bir dosyadaki hangi çağrılabilirlerin test sayılacağını belirler. Kural
değiştiğinde dosyanın içeriği değişmez, **koşan test kümesi** değişir. Ölçümdeki dosya beş
çağrılabilir taşır: üçü `t_` önekli, biri `kontrol_` önekli, biri de yardımcı bir işlev.

Bu üç kural gerçek bir durumu modelliyor. Bir takım büyüdükçe testler birden çok kişi
tarafından yazılır ve herkes aynı adlandırma alışkanlığını taşımaz; bir kısmı `test_`,
bir kısmı `t_`, bir kısmı da `kontrol_` gibi bir önek kullanır. Keşif kuralı bunlardan
yalnız birini arıyorsa öbürleri dosyada durur, gözle görünür, sürüm denetiminde
izlenir — ve hiç koşmaz.

Burada kritik olan şudur: koşmayan bir test **düşmüş sayılmaz**. Çerçeve onu hiç görmediği
için raporda yeri yoktur. Rapor "başarısız yok" der ve bu cümle hem beş testin koştuğu
durumda hem de hiçbirinin koşmadığı durumda aynıdır. Keşif kuralı bu yüzden bir ayrıntı
değil, kararın bir parçasıdır.

## Ölçümün Varsayımları

- **PT1** — Koşucu ders içinde modellenir: her testi çağırır, `AssertionError` yakalarsa
  testi düşmüş sayar, başka bir şey yapmaz. Ürün adı taşıyan hiçbir çerçeve kullanılmaz.
- **PT2** — Sınıf tabanlı takım standart kitaplığın `unittest` çerçevesiyle koşturulur;
  rapor akışı yutulur ve yalnız sayılar okunur.
- **PT3** — Üç test durumu ortak tanımın test kümesidir ve beklenen değerler işlevin
  **gözlenen davranışından** yazılmıştır, kâhinden değil.
- **PT4** — İndirim işlevinin davranışı değiştirilmez; ders yalnız test ve keşif kuralı
  ekler.
- **PT5** — Keşif kuralı bir ad önekidir ve testin gövdesine bakmaz. Üç kural denenir:
  `t_` öneki, `test_` öneki, ve `t_` ile `kontrol_` öneklerinin birleşimi.
- **PT6** — Dosyadaki `yardimci_tutar` bir test değil, bir yardımcıdır; hiçbir kural onu
  toplamaz ve toplasaydı iddia içermediği için geçmiş sayılırdı.
- **PT7** — Ayrı sonuç sayısı, bir ölçüm boyunca gözlenen **birbirinden farklı** sonuç
  demetlerinin sayısıdır; koşumda süre ölçülmez.
- **PT8** — Rapor yalnız düşen test sayısını taşır; koşmayan testin adı raporda yoktur ve
  raporu okuyan bunu göremez.

## Ölçüm

```python
"""Cerceve dort is yapar: kesif, kosum, iddia ve rapor. Kosucu ders icinde modellenir.

Bolum 1 - ayni uc durum, iki yazim bicimi.
Bolum 2 - ayni dosya, uc kesif kurali.
"""
import io
import unittest


def indirim(tutar, uye, kupon):
    """Ortak tanimin uc kararli islevi; davranisi degistirilmez."""
    oran = 0
    if uye:
        oran += 10
    if kupon:
        oran += 15
    if oran > 20:
        oran = 20
    return tutar - tutar * oran // 100


DURUMLAR = (("yalniz_uye", (100, True, False), 90),
            ("yalniz_kupon", (100, False, True), 85),
            ("ikisi_birden", (100, True, True), 80))


def kosucu(testler):
    """Modellenmis kosucu: her testi cagirir, gecen ve duseni sayar."""
    gecen, dusen = 0, []
    for ad, islev in testler:
        try:
            islev()
        except AssertionError:
            dusen.append(ad)
        else:
            gecen += 1
    return gecen, dusen


def duz_takim():
    def uret(arg, beklenen):
        def test():
            assert indirim(*arg) == beklenen
        return test
    return [(ad, uret(arg, bek)) for ad, arg, bek in DURUMLAR]


class SinifTakimi(unittest.TestCase):
    def test_yalniz_uye(self):
        self.assertEqual(indirim(100, True, False), 90)

    def test_yalniz_kupon(self):
        self.assertEqual(indirim(100, False, True), 85)

    def test_ikisi_birden(self):
        self.assertEqual(indirim(100, True, True), 80)


def sinif_kosumu():
    takim = unittest.TestLoader().loadTestsFromTestCase(SinifTakimi)
    sonuc = unittest.TextTestRunner(stream=io.StringIO(), verbosity=0).run(takim)
    return sonuc.testsRun - len(sonuc.failures) - len(sonuc.errors), len(sonuc.failures)


def t_yalniz_uye():
    assert indirim(100, True, False) == 90


def t_yalniz_kupon():
    assert indirim(100, False, True) == 85


def t_ikisi_birden():
    assert indirim(100, True, True) == 80


def kontrol_iki_yuz():
    assert indirim(200, True, True) == 160


def yardimci_tutar(tutar):
    return tutar


DOSYA = {"t_yalniz_uye": t_yalniz_uye, "t_yalniz_kupon": t_yalniz_kupon,
         "t_ikisi_birden": t_ikisi_birden, "kontrol_iki_yuz": kontrol_iki_yuz,
         "yardimci_tutar": yardimci_tutar}

KURALLAR = {"t_ öneki": ("t_",), "test_ öneki": ("test_",),
            "t_ ya da kontrol_": ("t_", "kontrol_")}


def kesif(kural):
    return [(ad, i) for ad, i in DOSYA.items() if ad.startswith(kural)]


print(f"{'yazım biçimi':<22s} {'test':>5s} {'geçen':>6s} {'düşen':>6s}")
duz_gecen, duz_dusen = kosucu(duz_takim())
print(f"{'düz işlev takımı':<22s} {len(DURUMLAR):5d} {duz_gecen:6d} {len(duz_dusen):6d}")
sinif_gecen, sinif_dusen = sinif_kosumu()
print(f"{'sınıf tabanlı takım':<22s} {len(DURUMLAR):5d} {sinif_gecen:6d} {sinif_dusen:6d}")
print(f"aynı üç durum, iki yazım biçimi: "
      f"{len({(duz_gecen, len(duz_dusen)), (sinif_gecen, sinif_dusen)})} ayrı sonuç")

print()
print(f"{'keşif kuralı':<18s} {'bulunan':>8s} {'geçen':>6s} {'düşen':>6s} {'rapor':>16s}")
bulunan, rapor = [], []
for ad, kural in KURALLAR.items():
    testler = kesif(kural)
    gecen, dusen = kosucu(testler)
    bulunan.append(len(testler))
    rapor.append(len(dusen))
    print(f"{ad:<18s} {len(testler):8d} {gecen:6d} {len(dusen):6d} "
          f"{'başarısız ' + str(len(dusen)):>16s}")
print(f"aynı dosya, üç keşif kuralı: bulunan test {len(set(bulunan))} ayrı sonuç, "
      f"rapor {len(set(rapor))} ayrı sonuç")
print(f"en dar kuralda hiç koşmayan test {max(bulunan) - min(bulunan)} tane; "
      f"rapor bunu söylemiyor")
```

```
yazım biçimi            test  geçen  düşen
düz işlev takımı           3      3      0
sınıf tabanlı takım        3      3      0
aynı üç durum, iki yazım biçimi: 1 ayrı sonuç

keşif kuralı        bulunan  geçen  düşen            rapor
t_ öneki                  3      3      0      başarısız 0
test_ öneki               0      0      0      başarısız 0
t_ ya da kontrol_         4      4      0      başarısız 0
aynı dosya, üç keşif kuralı: bulunan test 3 ayrı sonuç, rapor 1 ayrı sonuç
en dar kuralda hiç koşmayan test 4 tane; rapor bunu söylemiyor
```

## Sayıların Okunması

Üstteki tablo yazım biçiminin ödettiğini veriyor: aynı üç durum, iki ayrı yazımda
**1 ayrı sonuç**. Düz işlev takımı da sınıf tabanlı takım da 3 testin 3'ünü geçiriyor.
Yazım biçimi bir tercihtir ve kararı değiştirmez. Bu, çerçeveler arasında seçim yapmanın
önemsiz olduğu anlamına gelmez — ama seçimin ödettiği şey **sonuç değil, raporun
ayrıntısıdır.**

Alttaki tablo yapılandırmanın ödettiğini veriyor ve sayı değişiyor. Aynı dosya, üç keşif
kuralı: bulunan test **3 ayrı sonuç** (3, 0 ve 4), rapor ise **1 ayrı sonuç**. Üç kuralın
üçünde de rapor "başarısız 0" diyor.

İkinci satır bu ölçümün asıl bulgusudur. `test_` önekini arayan kural bu dosyada **hiçbir
test bulamıyor**, sıfır test koşuyor ve raporu "başarısız 0" oluyor. Bu rapor, üç testin
geçtiği rapordan **ayırt edilemez**. Bir takımın yeşil olması iki ayrı durumda doğrudur:
testler koştu ve geçti, ya da hiç test koşmadı.

Üçüncü satır aynı olguyu öbür yönden gösteriyor. `kontrol_iki_yuz` adlı test dosyada
duruyor, iddiası doğru ve geçiyor — ama yalnız `t_` önekini arayan varsayılan kural onu
hiç toplamıyor. En dar kural ile en geniş kural arasındaki fark **4 test**, ve bu farkın
tamamı raporun dışında kalıyor.

Buradan çıkan kural şudur: **bir test takımının kararı, testlerin kendisi kadar keşif
kuralına da bağlıdır.** Rapor koşan testleri sayar, koşmayanları değil; bu yüzden
raporun "başarısız 0" satırı tek başına bir kanıt değildir. Kanıtın tamamı için **bulunan
test sayısının** da okunması gerekir — ve bu sayı, ölçümün gösterdiği gibi, kodun kendisi
hiç değişmeden üç ayrı değer alabilir.

İki tablo birlikte okunduğunda kursun ölçü ekseni burada ilk kez görünür hale geliyor.
Aynı girdiye — aynı üç durum, aynı işlev, aynı dosya — iki ayrı soru soruldu. Birincisi
"nasıl yazdın" sorusuydu ve sonucu değiştirmedi: **1 ayrı sonuç**. İkincisi "neyi
koşturuyorsun" sorusuydu ve sonucu değiştirdi: **3 ayrı sonuç**. Yeniden üretilebilirliği
tehdit eden şey birincisi değil ikincisidir, çünkü ikincisi bir dosyada değil, koşturmayı
yapılandıran ayarda yazılıdır ve kodla birlikte okunmaz.

Bu ayrımın pratikteki karşılığı da açıktır. Bir takımın yeşil raporu, ancak koşan test
sayısı da bilindiğinde bir bilgi taşır; sayı bilinmeden okunan yeşil rapor, hiçbir testin
koşmadığı bir kurulumda da aynı görünür. Bu yüzden koşan test sayısı raporun yanında
tutulur ve düşmesi, düşen test kadar dikkat çeken bir olay sayılır.

## Özet

- Bir test çerçevesi dört iş yapar: keşif, koşum, iddia ve rapor. Bunlardan yalnız
  **iddia** doğruluk hakkında bir şey söyler; öbür üçü yapılandırma kararıdır.
- İddia, beklenen değeri bir kâhinden alır. Testin geçmesi, o beklenen değerin doğru
  olduğunu göstermez.
- Aynı üç durum düz işlev takımıyla da sınıf tabanlı takımla da **3/3** geçer; iki yazım
  biçimi **1 ayrı sonuç** verir ve fark yalnız raporun ayrıntısındadır.
- Aynı dosya üç keşif kuralında **3, 0 ve 4** test bulur — **3 ayrı sonuç**; rapor
  üçünde de "başarısız 0" der ve **1 ayrı sonuçta** kalır.
- Hiç koşmayan test düşmüş sayılmaz; en dar kuralda **4 test** raporun dışında kalır ve
  yeşil bir rapor bunu söylemez.

## Sonraki Adım

Keşif kuralı hangi testlerin koşacağını belirledi; ama koşan testler tek tek bağımsız
değil. Ortak kurgunun beş testlik takımında biri paylaşılan durumu kirletiyor, biri de
kirlenmemiş bir durum varsayıyor. Böyle bir takımda testleri hangi sırayla çağırdığınız
bir yapılandırma ayrıntısıdır — peki bu ayrıntı kararı değiştiriyor mu? Sonraki ders aynı
takımı üç ayrı sırada koşturur, çıkan ayrı karar sayısını okur ve tekrarı testten çıkaran
iki düzeneği ölçer: paylaşılan durumu her testte yeniden kuran **düzenek** ve aynı gövdeyi
birden çok girdiyle koşturan **parametreleme**.
