---
title: 'Kapsam Ölçümü'
source: 'https://academia.sh/tr/kurslar/python-projeleri/kapsam-olcumu'
course: 'Python Projeleri: Paketleme ve Test'
language: tr
updated: '2026-08-17T18:10:31+00:00'
license: 'CC BY-SA 4.0'
---

# Kapsam Ölçümü

Üç testlik küme sekiz satırın sekizini kapsar (1,0000), altı dalın altısını alır ve 3/3 geçer; buna karşın dört girdi bileşiminden birinde gerçek davranış kâhinden ayrılır ve test kümesi o çağrıyı çalıştırır — kapsam, testin neye baktığını değil, kodun nerede yürüdüğünü sayar.

Önceki ders belge iddialarını sınanabilir hale getirdi ve on iki iddianın bayatlayan
yedisinden dördünün, sonra altısının sese döndüğünü ölçtü. Oradaki soru "iddia
yanlışlandığında duyuluyor mu" sorusuydu. Şimdi ters yönden bakma sırası: testler koştuğunda
**kodun neresi yürüdü?**

Bu soruyu yanıtlayan sayıya **kapsam (coverage)** denir. Kapsam bir test takımının kaliteli
olup olmadığını söylemez; bir koşum boyunca kodun hangi bölümlerinin çalıştığını sayar.
Bu dersin işi o sayının tam olarak neyi ölçtüğünü göstermek ve sınırını çizmektir — çünkü
sınırın yanlış çizilmesi, bütün konudaki en pahalı okuma hatasıdır.

## Kapsam Sayacı Neyi Sayar

Ölçümdeki kapsam sayacı ders içinde modellenmiştir; hiçbir araç adı geçmez. Modelin
dayandığı düzenek Python'un **izleme kancasıdır**: yorumlayıcıya bir işlev verilir ve
yorumlayıcı, yürütmenin belli olaylarında o işlevi çağırır. Olaylardan biri `line`
olayıdır ve her yeni kaynak satırına geçildiğinde tetiklenir.

Sayaç bundan ibarettir. Kanca çağrıldığında yürüyen satırın numarası kaydedilir; koşum
bittiğinde kaydedilen numaralar kümesi, kapsanan satırları verir. Kancanın gördüğü bilgi
yalnızca "hangi satırdayız" bilgisidir — hangi test koşuyor, iddia ne bekliyor, sonuç doğru
mu, hiçbiri kancaya gelmez.

Bu, ölçümün bütün sonucunu önceden belirleyen bir olgudur ve bir eksiklik değildir.
İzleme kancası testin beklentisini göremez, çünkü beklenti testin içinde bir
karşılaştırmadır ve kanca yürütmeyi izler, anlamı değil. **Kapsam sayacı, yürütmenin
coğrafyasını çıkarır.**

İşlevin sekiz satırı, gövdedeki sekiz deyime karşılık gelir ve numaraları kodun kendisinden
okunur: tanım satırı ile belge dizgisi dışarıda bırakılır, geriye yürütülebilir sekiz satır
kalır.

## İki Ölçüt: Satır ve Dal

**Satır kapsamı** bir satırın en az bir kez yürüyüp yürümediğini sorar. **Dal kapsamı**
bir koşulun hem doğru hem yanlış sonucunun alınıp alınmadığını sorar. İkisi arasındaki
fark, koşullu bir deyimin gövdesi hiç çalışmadığında ortaya çıkar: satır kapsamı koşul
satırını kapsanmış sayar, dal kapsamı yalnız bir dalın alındığını kaydeder.

Bu ölçütler ve aralarındaki hiyerarşi Yazılım Kalitesi ve Test müfredatının Birim Testi ve
Test Odaklı Geliştirme kursunda kuram olarak kurulmuştur; **burada tekrarlanmıyor**, atıf
yapılıyor. Bu derste ikisi birlikte ölçülüyor, çünkü iddianın gücü buna bağlı: eğer yalnız
satır kapsamı tam olsaydı "daha güçlü bir ölçüt kullansaydınız görürdünüz" denebilirdi.
Ölçüm bu itirazı kapatıyor — **iki ölçüt de tamdır ve kusur yine görünmez.**

Dal kapsamı modelde de yürütmeden çıkarılır: her koşul için, o koşulun gövdesindeki satırın
o çağrıda yürüyüp yürümediğine bakılır. Yürüdüyse doğru dal, yürümediyse yanlış dal
alınmıştır. Üç koşul, altı dal.

Ölçütlerin ötesinde bir üçüncüsü de vardır ve burada kullanılmıyor: **yol kapsamı**, yani
koşulların birbirinden bağımsız bileşimlerinin kaçının denendiği. Üç koşulun bu işlevde
dört ulaşılabilir bileşimi var ve ölçümün ikinci tablosu tam olarak o dördünü listeliyor.
Bu ders yol kapsamını ayrı bir sayı olarak yazmıyor, çünkü kanıtı zayıflatmıyor
güçlendiriyor: dördün üçü test kümesinde ve kusurlu olan da onların arasında.

## Kâhin Ayrı Yazılmıştır

Ortak kurgunun indirim işlevi üç karar verir: üyelik on puan, kupon on beş puan, ve bir
tavan. Kusur üçüncü karardadır — tavan **20** olarak yazılmıştır, oysa sözleşme **25**
demektedir. Kâhin, yani doğru davranış, ayrı bir işlev olarak yazılmıştır ve tavanı 25
uygular.

Test kümesinin beklenen değerleri ise kâhinden değil, **işlevin gözlenen davranışından**
yazılmıştır. Bu, ilk derste kurulan durumun aynısıdır ve bir kurgu ayrıntısı değil, gerçek
bir yazım alışkanlığıdır: var olan bir işleve test yazarken beklenen değerler çoğu kez
işlevi çalıştırıp çıktısına bakarak belirlenir. Böyle yazılmış bir test **davranışın
değişmediğini** doğrular; doğru olduğunu değil.

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

- **PT41** — İndirim işlevi ve kâhin ortak tanımdan alınır; ikisinin de davranışı
  değiştirilmez. Ders yalnız kapsam sayacını ve bir dördüncü testi ekler.
- **PT42** — Kapsam sayacı izleme kancasıyla modellenir. Kanca yalnız `line` olayını ve
  yalnız indirim işlevinin çerçevesini sayar; başka hiçbir bilgiye erişmez.
- **PT43** — Sayılan sekiz satır, işlevin gövdesindeki yürütülebilir deyimlerdir ve
  numaraları kodun kendisinden okunur; tanım satırı ile belge dizgisi dışarıdadır.
- **PT44** — Dal kapsamı üç koşulun her biri için iki sonuçtan oluşur, toplam altı dal.
  Bir dalın alındığı, o koşulun gövdesindeki satırın yürümesinden çıkarılır.
- **PT45** — Test kümesi ortak tanımın üç testidir ve beklenen değerleri işlevin gözlenen
  davranışından yazılmıştır.
- **PT46** — Kâhin karşılaştırması ölçümün dışında, ayrı bir tabloda yapılır; test kümesi
  kâhini hiç çağırmaz.
- **PT47** — Girdi uzayı tutar 100'de sabit tutulur ve iki mantıksal bayrağın dört
  bileşimi denenir. Tutarın değişmesi kusuru değil, kusurun büyüklüğünü değiştirir.
- **PT48** — Bir çağrının "kümede" olması, test kümesindeki üç girdiden birine eşit olması
  demektir.
- **PT49** — Dördüncü test aynı girdiyi kullanır ama beklenen değerini **kâhinden** alır;
  başka hiçbir farkı yoktur.
- **PT50** — Geçen test sayısı, beklenen değerle gerçekleşen değerin eşit olduğu test
  sayısıdır; kısmi geçme yoktur.
- **PT51** — Oranlar dört ondalık basamakla yazılır; bu kümede ölçülebilen en küçük satır
  farkı 1/8, yani 0,1250'dir.
- **PT52** — Süre ölçülmez, depoya dosya bırakılmaz. Sayılan şey kapsanan satır, alınan
  dal, geçen test ve kâhinden sapan çağrı sayısıdır.

## Ölçüm

```python
"""Kapsam sayaci: hangi satir yurudu, hangi dal alindi, hangi cagri kahinden sapti.

Sayac izleme kancasiyla ders icinde modellenir; olculen sey kodun nerede yurudugudur.
"""
import sys


def indirim(tutar, uye, kupon):
    """Uc kararlı islev; kusur ucuncu karardadir."""
    oran = 0
    if uye:
        oran += 10
    if kupon:
        oran += 15
    if oran > 20:
        oran = 20
    return tutar - tutar * oran // 100


def dogru_indirim(tutar, uye, kupon):
    """Kahin: dogru davranis tavani 25 olarak uygular."""
    oran = (10 if uye else 0) + (15 if kupon else 0)
    if oran > 25:
        oran = 25
    return tutar - tutar * oran // 100


AD = ("oran=0", "if uye", "oran+=10", "if kupon", "oran+=15",
      "if oran>20", "oran=20", "return")
GOVDE = indirim.__code__.co_firstlineno + 1   # tanım ve belge dizgisi satırı dışarıda
NO = sorted({s for _, _, s in indirim.__code__.co_lines()
             if s is not None and s > GOVDE})
SATIR = dict(zip(NO, AD))
DALLAR = {"if uye": "oran+=10", "if kupon": "oran+=15", "if oran>20": "oran=20"}

TEST_KUMESI = (((100, True, False), 90),
               ((100, False, True), 85),
               ((100, True, True), 80))
HEPSI = [(100, u, k) for u in (False, True) for k in (False, True)]


def izleyerek(arg):
    """Tek cagriyi izler ve yuruyen satirlarin adini dondurur."""
    gorulen = set()

    def kanca(cerceve, olay, ek):
        if cerceve.f_code is indirim.__code__:
            if olay == "line":
                gorulen.add(SATIR[cerceve.f_lineno])
            return kanca
        return None

    sys.settrace(kanca)
    try:
        deger = indirim(*arg)
    finally:
        sys.settrace(None)
    return deger, gorulen


def kapsam(cagrilar):
    """Satir ve dal kapsami: sayilan sey satirin yuruyup yurumedigidir."""
    satir, dal = set(), set()
    for arg in cagrilar:
        _, gorulen = izleyerek(arg)
        satir |= gorulen
        for kosul, govde in DALLAR.items():
            dal.add((kosul, govde in gorulen))
    return satir, dal


def gecen(kume):
    return sum(1 for arg, beklenen in kume if izleyerek(arg)[0] == beklenen)


cagrilar = [a for a, _ in TEST_KUMESI]
satir, dal = kapsam(cagrilar)
sapan = [a for a in HEPSI if indirim(*a) != dogru_indirim(*a)]

print(f"{'ölçüt':<14s} {'kapsanan':>9s} {'toplam':>7s} {'oran':>7s}")
print(f"{'satır':<14s} {len(satir):9d} {len(AD):7d} {len(satir) / len(AD):7.4f}")
print(f"{'dal':<14s} {len(dal):9d} {2 * len(DALLAR):7d} "
      f"{len(dal) / (2 * len(DALLAR)):7.4f}")
print(f"geçen test {gecen(TEST_KUMESI)}/{len(TEST_KUMESI)}")

print()
print(f"{'girdi':<24s} {'gerçek':>7s} {'kâhin':>7s} {'sapma':>6s} {'kümede':>7s}")
for a in HEPSI:
    g, d = indirim(*a), dogru_indirim(*a)
    print(f"{'tutar=%d üye=%d kupon=%d' % (a[0], a[1], a[2]):<24s} {g:7d} {d:7d} "
          f"{('var' if g != d else 'yok'):>6s} "
          f"{('evet' if a in cagrilar else 'hayır'):>7s}")
print(f"dört bileşimden {len(sapan)} tanesinde gerçek davranış kâhinden ayrılıyor; "
      f"o çağrı kümede {'var' if sapan[0] in cagrilar else 'yok'}")

print()
DORTLU = TEST_KUMESI + (((100, True, True), dogru_indirim(100, True, True)),)
satir4, dal4 = kapsam([a for a, _ in DORTLU])
print(f"{'test kümesi':<16s} {'test':>5s} {'geçen':>6s} {'satır':>7s} {'dal':>7s}")
print(f"{'üç test':<16s} {len(TEST_KUMESI):5d} {gecen(TEST_KUMESI):6d} "
      f"{len(satir) / len(AD):7.4f} {len(dal) / (2 * len(DALLAR)):7.4f}")
print(f"{'kâhinli dördüncü':<16s} {len(DORTLU):5d} {gecen(DORTLU):6d} "
      f"{len(satir4) / len(AD):7.4f} {len(dal4) / (2 * len(DALLAR)):7.4f}")
ORAN = {(gecen(TEST_KUMESI), len(TEST_KUMESI)), (gecen(DORTLU), len(DORTLU))}
print(f"dördüncü test kapsamı {len({len(satir), len(satir4)})} ayrı sonuçta bırakıyor, "
      f"geçen oranını {len(ORAN)} ayrı sonuca çıkarıyor")
```

```
ölçüt           kapsanan  toplam    oran
satır                  8       8  1.0000
dal                    6       6  1.0000
geçen test 3/3

girdi                     gerçek   kâhin  sapma  kümede
tutar=100 üye=0 kupon=0      100     100    yok   hayır
tutar=100 üye=0 kupon=1       85      85    yok    evet
tutar=100 üye=1 kupon=0       90      90    yok    evet
tutar=100 üye=1 kupon=1       80      75    var    evet
dört bileşimden 1 tanesinde gerçek davranış kâhinden ayrılıyor; o çağrı kümede var

test kümesi       test  geçen   satır     dal
üç test              3      3  1.0000  1.0000
kâhinli dördüncü     4      3  1.0000  1.0000
dördüncü test kapsamı 1 ayrı sonuçta bırakıyor, geçen oranını 2 ayrı sonuca çıkarıyor
```

## Tam Kapsam ve Kusurlu Davranış

Üstteki tablo üç sayıyı yan yana koyuyor ve üçü de olumlu görünüyor. Üç testlik küme sekiz
satırın **sekizini** kapsıyor: **1,0000**. Altı dalın **altısını** alıyor: **1,0000**.
Ve **3/3** test geçiyor. Bu üç sayıya bakan biri işlevin sınandığı sonucuna varır.

Ortadaki tablo aynı işlevin bütün girdi uzayını veriyor ve son satırda sapma duruyor.
Üyelik ve kupon birlikte geldiğinde işlev **80**, kâhin **75** veriyor — oran 25 olması
gerekirken tavan onu 20'ye çekiyor. **Dört girdi bileşiminden 1'inde** gerçek davranış
kâhinden ayrılıyor.

İki tablo arasındaki bağ ölçümün asıl bulgusudur. Sapan çağrı — üyelik ve kupon birlikte —
test kümesinin **içinde**. Yani test kümesi o çağrıyı **çalıştırıyor**. Kusurun bulunduğu
satır — tavanı uygulayan satır — yürüyor; kapsam sayacı onu görüyor ve kapsanan olarak
kaydediyor. Kapsamın 1,0000 çıkmasının nedeni de zaten bu çağrının kümede olması.

Öyleyse test neden geçiyor? Çünkü o testin beklediği değer **80**. Beklenen değer işlevin
gözlenen davranışından yazıldığı için, işlev kusurlu değerini üretiyor ve karşılaştırma
tutuyor. **Geçmesinin nedeni kapsamın eksikliği değil, beklentinin kusurlu değeri yazmış
olmasıdır.**

Kursun üçüncü iddiası tam olarak budur: **kapsam satırı görür, beklentiyi görmez.** Ve
nedeni bu dersin ilk bölümünde yazılıydı: izleme kancasına gelen bilgi yalnız "hangi
satırdayız" bilgisidir. Testin beklediği değer o kancaya hiç ulaşmaz.

## Aynı Kapsam, Ayrı Karar

Alttaki tablo bunu bir deneyle kesinleştiriyor. Üç testlik kümeye dördüncü bir test
ekleniyor; girdisi aynı — üyelik ve kupon birlikte — ama beklenen değeri **kâhinden**
alınıyor. Yeni test düşüyor ve küme **3/4**'e iniyor.

Kapsam ise kılını kıpırdatmıyor: satır **1,0000**, dal **1,0000**. Aynı girdi zaten
kümedeydi, dolayısıyla yürüyen satır kümesi hiç değişmedi. Kapsam **1 ayrı sonuçta**
kalıyor, geçen oran ise **2 ayrı sonuca** çıkıyor.

Bu iki sayının ayrışması, kapsamın ne olduğunun en yalın kanıtıdır. Kusuru bulan
değişiklik kapsamı **hiç** değiştirmedi. Kapsamı büyüterek kusura yaklaşmak bu örnekte
imkânsızdır, çünkü kapsam zaten tavandadır. Eksik olan şey bir çağrı değil, bir
**beklentiydi** — ve beklenti kapsamın ölçtüğü şeyin dışındadır.

Tersinin de doğru olduğunu söylemek gerekiyor: kapsamın düşük olması bir bilgidir ve
gerçek bir bilgidir. Yürümemiş bir satır hiçbir testin denemediği bir satırdır ve orada
her şey olabilir. Kapsam **hangi kodun sınanmadığını** güvenilir biçimde söyler; **hangi
kodun doğru olduğunu** hiç söylemez. Sayının yönü tek taraflıdır.

Bu asimetrinin bir sonucu daha var. Bir satırın yürümesi ile o satırın sınanması ayrı
şeylerdir, ve aradaki farkı kapatan tek şey bir iddiadır. Kapsam sayacına göre bir satır,
hiçbir iddiası olmayan bir test onu çalıştırdığında da kapsanmıştır — ilk derste görülen
iddiasız test tam olarak bunu yapar. Yani kapsamı yükseltmek için kodu çağırmak yeterlidir
ve sonucu hiç kontrol etmek gerekmez; sayı ikisini ayırt edemez.

## Sayı Nasıl Okunur

Buradan üç okuma kuralı çıkıyor.

Birincisi: **kapsam bir alt sınırdır, bir belge değil.** Düşük kapsam bir eksikliğin
kanıtıdır; yüksek kapsam bir yeterliliğin kanıtı değildir. Bu asimetri, sayının hedef
olarak konulduğunda neden aşındığını da açıklar — sayıyı yükseltmenin en ucuz yolu iddia
eklemek değil, çağrı eklemektir.

İkincisi: **kapsam yüzdesi tek başına karşılaştırılamaz.** Bu ölçümde en küçük ölçülebilir
satır farkı 1/8, yani **0,1250**'dir; sekiz satırlık bir işlevde 1,0000 ile 0,8750
arasında ara değer yoktur. Başka bir kod tabanının yüzdesiyle yan yana konan bir kapsam
sayısı, iki tarafın da neyi saydığı yazılmadan bir şey ifade etmez. Bu, bir önceki kursun
ölçüm disiplini dersinde kurulan kuralın bu ölçüme uygulanmasıdır.

Üçüncüsü: **kapsam beklentinin kalitesini ölçen bir sayı değildir ve onu ölçen bir sayı
kapsam sayacından çıkmaz.** Beklentinin doğruluğunu belirleyen şey kâhindir — sözleşme,
belirtim, ya da bu kursta olduğu gibi kurgunun kendisi. Kâhin yoksa yazılan her beklenti
gözlenen davranışı yeniden yazar ve takım, kodun bugünkü halini kendine kanıt olarak
sunar.

## Özet

- Kapsam sayacı izleme kancasının `line` olayını sayar; gördüğü tek bilgi hangi satırda
  olunduğudur. Testin beklentisi bu kancaya hiç ulaşmaz.
- Üç testlik küme sekiz satırın **sekizini** kapsar (**1,0000**), altı dalın **altısını**
  alır (**1,0000**) ve **3/3** geçer.
- Buna karşın dört girdi bileşiminden **1**'inde gerçek davranış kâhinden ayrılır ve test
  kümesi **o çağrıyı çalıştırır**; geçmesinin nedeni kapsamın eksikliği değil,
  beklentinin kusurlu değeri yazmış olmasıdır.
- Kâhinden beklenti alan bir dördüncü test kümeyi **3/4**'e indirir ama kapsamı
  değiştirmez: kapsam **1 ayrı sonuçta** kalır, geçen oran **2 ayrı sonuca** çıkar.
- Kapsam tek yönlü bir bilgidir: hangi kodun sınanmadığını söyler, hangi kodun doğru
  olduğunu söylemez. **Kapsam, testin neye baktığını değil, kodun nerede yürüdüğünü
  sayar.**

## Sonraki Adım

Bu konu boyunca ölçülen her şey kodun **çalışmasıyla** ilgiliydi: hangi testler bulundu,
hangi sırada koştular, hangi kaynağa baktılar, hangi iddia sese döndü, hangi satır yürüdü.
Kapsam bu dizinin sonuncusuydu ve kodun nerede yürüdüğünü saydı.

Geriye kodun bir başka yüzü kalıyor: **nasıl yazıldığı.** Aynı davranışı üreten bir işlev
farklı girintiyle, farklı satır sonlarıyla, farklı tırnak ve boşluk kararlarıyla sayısız
biçimde yazılabilir ve bunların hiçbiri kapsam sayacında görünmez — yürüyen satırlar aynı
satırlardır. Bu biçim kararları bir insana bırakıldığında her inceleme yeni bir tartışma
üretir. Sonraki ders soruyu buraya taşır: aynı kaynağın kaç ayrı yazımı varken kaça iner,
ve bu tekleştirmeyi kim yapar?
