---
title: 'Kapsam Ölçümü'
source: 'https://academia.sh/tr/kurslar/birim-testi/kapsam-olcumu'
course: 'Birim Testi ve Test Odaklı Geliştirme'
language: tr
updated: '2026-08-17T18:10:54+00:00'
license: 'CC BY-SA 4.0'
---

# Kapsam Ölçümü

Satır, dal ve yol kapsamının aynı işlev üzerinde elle sayılması, yüzde yüz satır kapsamına ulaşmış bir takımın kaçırdığı hatanın koşturularak gösterilmesi ve kapsam yüzdesinin bir hedef olarak kullanılmasının sınırı.

Önceki dersler testlerin **neyi** sınadığını ayırdı: hangi iddia hangi bozulmayı yakalar,
hangi sahte bağımlılık hangi soruyu yanıtlar. Sorulmayan soru şudur: yazılan testler
kitaplığın ne kadarına dokundu?

Bu soruya verilen yaygın yanıt bir yüzdedir ve o yüzde en az üç farklı biçimde hesaplanır.
Üç ölçüm aynı kod üzerinde farklı sayılar verir; aralarındaki fark, ölçümün ne söylediğini
belirler. Bu ders üçünü aynı işlev üzerinde elle sayar.

## Ölçülecek İşlev

Gecikme ücreti hesabı iki koşul içerir: ücretsiz gün payının aşılması ve kitabın ödünç
alındığı şubeden başka bir şubeye iade edilmesi.

```js
// ucret.mjs — gecikme ve sube disi iade ucreti
export const GECIKME = { ucretsizGun: 3, gunlukUcret: 2, ustSinir: 40, subeDisiUcret: 5 };

export function gecikmeUcreti(kayit, iade) {
  const gecikme = iade.gun - kayit.sonGun;
  let ucret = 0;
  if (gecikme > GECIKME.ucretsizGun) {
    ucret = (gecikme - GECIKME.ucretsizGun) * GECIKME.gunlukUcret;
  }
  if (iade.sube !== kayit.sube) {
    ucret += GECIKME.subeDisiUcret;
  }
  return Math.min(ucret, GECIKME.ustSinir);
}
```

İki test yazılmış. İkisi de geçiyor.

```js
// kapsam.test.mjs — iki test, yuzde yuz satir kapsami
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { gecikmeUcreti } from './ucret.mjs';

const KAYIT = { sonGun: 1014, sube: 'merkez' };

test('gecikmis iade gun basina ucretlendirilir', () => {
  assert.equal(gecikmeUcreti(KAYIT, { gun: 1024, sube: 'merkez' }), 14);
});

test('gecikmis sube disi iadeye ek ucret eklenir', () => {
  assert.equal(gecikmeUcreti(KAYIT, { gun: 1024, sube: 'yamac' }), 19);
});
```

```sh
node --test --test-reporter=tap kapsam.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
```

```
ok 1 - gecikmis iade gun basina ucretlendirilir
ok 2 - gecikmis sube disi iadeye ek ucret eklenir
# tests 2
# pass 2
# fail 0
```

## Üç Ölçümün Elle Sayımı

Kapsam ölçümü, sayılacak birim seçildiğinde tanımlanır.

**Satır kapsamı** (statement coverage) çalıştırılan satırların toplam satıra oranıdır.
**Dal kapsamı** (branch coverage) her koşulun hem doğru hem yanlış sonucunun görülüp
görülmediğini sayar. **Yol kapsamı** (path coverage) koşul sonuçlarının **birleşimlerini**
sayar: iki koşullu bir işlevde dört, üç koşulluda sekiz yol vardır.

Aynı işlevin iz tutan bir kopyası bu üç sayıyı doğrudan verir.

```js
// olcum.mjs — ayni islevin satir, dal ve yol izini tutan surumu
import { GECIKME } from './ucret.mjs';

const satirlar = new Set();
const dallar = new Set();
const yollar = new Set();

function izlenenUcret(kayit, iade) {
  satirlar.add('S1');
  const gecikme = iade.gun - kayit.sonGun;
  satirlar.add('S2');
  let ucret = 0;
  satirlar.add('S3');
  const d1 = gecikme > GECIKME.ucretsizGun;
  dallar.add(d1 ? 'D1-dogru' : 'D1-yanlis');
  if (d1) {
    satirlar.add('S4');
    ucret = (gecikme - GECIKME.ucretsizGun) * GECIKME.gunlukUcret;
  }
  satirlar.add('S5');
  const d2 = iade.sube !== kayit.sube;
  dallar.add(d2 ? 'D2-dogru' : 'D2-yanlis');
  if (d2) {
    satirlar.add('S6');
    ucret += GECIKME.subeDisiUcret;
  }
  satirlar.add('S7');
  yollar.add(`${d1 ? 'D' : 'Y'}${d2 ? 'D' : 'Y'}`);
  return Math.min(ucret, GECIKME.ustSinir);
}

const KAYIT = { sonGun: 1014, sube: 'merkez' };
izlenenUcret(KAYIT, { gun: 1024, sube: 'merkez' });
izlenenUcret(KAYIT, { gun: 1024, sube: 'yamac' });

const TUM_SATIR = ['S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7'];
const TUM_DAL = ['D1-dogru', 'D1-yanlis', 'D2-dogru', 'D2-yanlis'];
const TUM_YOL = ['DD', 'DY', 'YD', 'YY'];

const rapor = (ad, tumu, gorulen) => {
  const eksik = tumu.filter((x) => !gorulen.has(x));
  const oran = Math.round(((tumu.length - eksik.length) / tumu.length) * 100);
  console.log(`${ad} ${tumu.length - eksik.length}/${tumu.length} (%${oran})  eksik: ${eksik.join(', ') || 'yok'}`);
};

rapor('satir kapsami', TUM_SATIR, satirlar);
rapor('dal kapsami  ', TUM_DAL, dallar);
rapor('yol kapsami  ', TUM_YOL, yollar);
```

```
satir kapsami 7/7 (%100)  eksik: yok
dal kapsami   3/4 (%75)  eksik: D1-yanlis
yol kapsami   2/4 (%50)  eksik: YD, YY
```

Aynı iki test, aynı işlev, üç farklı sayı. Satır ölçümü tam görünüyor: yedi satırın yedisi
de çalıştı. Dal ölçümü bir eksik bildiriyor: birinci koşulun yanlış sonucu — yani gecikmesiz
bir iade — hiç denenmedi. Yol ölçümü iki eksik bildiriyor: gecikmesiz iadenin iki şube
durumu da hiç oluşmadı.

Üç sayının sırası rastlantı değildir. Satır kapsamı her zaman dal kapsamından büyük ya da
ona eşittir; dal kapsamı da yol kapsamından. Bir satırın çalışması için bir dalın
girilmesi yeter, ama bir dalın her iki sonucunun görülmesi için ayrı girdiler gerekir ve
yolların birleşimi için daha da fazlası.

## Yüzde Yüz Satır Kapsamının Kaçırdığı

Kapsanmayan iki yol boş bir olasılık değil, çalıştırılabilir birer girdidir.

```js
// kacan.mjs — kapsanmayan yollarda islevin verdigi sonuclar
import { gecikmeUcreti } from './ucret.mjs';

const KAYIT = { sonGun: 1014, sube: 'merkez' };
const DURUMLAR = [
  ['YD  gecikmesiz, sube disi', { gun: 1016, sube: 'yamac' }, 0],
  ['YY  gecikmesiz, ayni sube', { gun: 1016, sube: 'merkez' }, 0],
];

for (const [ad, iade, beklenen] of DURUMLAR) {
  const gercek = gecikmeUcreti(KAYIT, iade);
  console.log(`${ad}: ucret ${gercek}, beklenen ${beklenen} -> ${gercek === beklenen ? 'uyumlu' : 'HATALI'}`);
}
```

```
YD  gecikmesiz, sube disi: ucret 5, beklenen 0 -> HATALI
YY  gecikmesiz, ayni sube: ucret 0, beklenen 0 -> uyumlu
```

Yazılı kural şubeler arası iade ücretini gecikme ücretinin bir eki olarak tanımlar:
zamanında iade edilen bir kitap, hangi şubeye bırakılırsa bırakılsın ücretsizdir.
Gerçekleştirim ise şube denetimini gecikmeden bağımsız uyguluyor ve zamanında iade eden
üyeden beş birim alıyor.

Bu hata, satır kapsamı yüzde yüz olan bir takımın altında duruyordu. Ölçüm yanlış değildi;
**sorduğu soru** dardı. Satır ölçümü "bu satır çalıştı mı" diye sorar, "bu satır hangi
koşullar altında çalıştı" diye sormaz.

Eksik yollar teste çevrildiğinde hata rapora düşer.

```js
// kapsam.test.mjs — surum 2: dort yolun dordu de sinaniyor
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { gecikmeUcreti } from './ucret.mjs';

const KAYIT = { sonGun: 1014, sube: 'merkez' };

test('gecikmis iade gun basina ucretlendirilir', () => {
  assert.equal(gecikmeUcreti(KAYIT, { gun: 1024, sube: 'merkez' }), 14);
});

test('gecikmis sube disi iadeye ek ucret eklenir', () => {
  assert.equal(gecikmeUcreti(KAYIT, { gun: 1024, sube: 'yamac' }), 19);
});

test('ucretsiz gun payi icindeki iade ucretsizdir', () => {
  assert.equal(gecikmeUcreti(KAYIT, { gun: 1016, sube: 'merkez' }), 0);
});

test('ucretsiz gun payi icindeki sube disi iade de ucretsizdir', () => {
  assert.equal(gecikmeUcreti(KAYIT, { gun: 1016, sube: 'yamac' }), 0);
});
```

```sh
node --test --test-reporter=tap kapsam.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
```

```
ok 1 - gecikmis iade gun basina ucretlendirilir
ok 2 - gecikmis sube disi iadeye ek ucret eklenir
ok 3 - ucretsiz gun payi icindeki iade ucretsizdir
not ok 4 - ucretsiz gun payi icindeki sube disi iade de ucretsizdir
# tests 4
# pass 3
# fail 1
```

Düzeltme, ücretsiz gün payı içindeki iadeyi erken dönüşle ayırır.

```js
// ucret.mjs — surum 2: sube disi ucreti yalniz gecikmis iadede alinir
export const GECIKME = { ucretsizGun: 3, gunlukUcret: 2, ustSinir: 40, subeDisiUcret: 5 };

export function gecikmeUcreti(kayit, iade) {
  const gecikme = iade.gun - kayit.sonGun;
  if (gecikme <= GECIKME.ucretsizGun) return 0;
  let ucret = (gecikme - GECIKME.ucretsizGun) * GECIKME.gunlukUcret;
  if (iade.sube !== kayit.sube) {
    ucret += GECIKME.subeDisiUcret;
  }
  return Math.min(ucret, GECIKME.ustSinir);
}
```

```sh
node --test --test-reporter=tap kapsam.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
```

```
ok 1 - gecikmis iade gun basina ucretlendirilir
ok 2 - gecikmis sube disi iadeye ek ucret eklenir
ok 3 - ucretsiz gun payi icindeki iade ucretsizdir
ok 4 - ucretsiz gun payi icindeki sube disi iade de ucretsizdir
# tests 4
# pass 4
# fail 0
```

Erken dönüşün ikinci bir etkisi vardır: ücretsiz gün payı içindeki iadede şube koşulu artık
hiç değerlendirilmez. Dört yol üçe iner, çünkü iki yol tek bir çıkışta birleşir. Yol
sayısını azaltmak, kapsamı artırmanın test yazmaktan başka bir yoludur ve genellikle daha
ucuzdur.

## Yüzdenin Okunması

Kapsam yüzdesinin doğru okunuşu tek yönlüdür. Düşük bir kapsam **kesin** bilgi verir:
kodun bir bölümü hiç çalıştırılmadı. Yüksek bir kapsam ise hiçbir şeyin kanıtı değildir;
kodun çalıştırıldığını söyler, doğru davrandığını değil. İkinci derste ölçülen totoloji
testi yüzde yüz kapsam üretir ve tek bir bozulmayı yakalamaz.

Bundan iki uygulama sonucu çıkar. Kapsam bir **hedef** olarak kullanıldığında ölçüt bozulur:
sayıyı yükseltmenin en ucuz yolu iddiasız test yazmaktır ve bu, sinyal üretmeden yüzdeyi
yükseltir. Kapsam bir **liste** olarak kullanıldığında ise doğrudan işe yarar: kapsanmayan
dal ve yollar, henüz sorulmamış soruların adıdır. Yukarıdaki hata tam olarak o listeden
çıktı.

Yerleşik test koşucusunun kapsam raporu üreten bir seçeneği vardır. Bu seçeneğin adı,
raporun biçimi ve deneysel olup olmadığı çalışma zamanı sürümüne bağlıdır; ürettiği tablo
mutlak dosya yollarını ve çalıştırılan ortamın ayrıntılarını içerir. Bu yüzden burada
kapsam elle sayıldı: ölçümün **tanımı** araçtan bağımsızdır, çıktısı değildir.

## Özet

- Kapsam ölçümü sayılacak birim seçildiğinde tanımlanır; satır, dal ve yol kapsamı aynı kod
  üzerinde farklı sayılar verir.
- Ölçümde iki test satır kapsamını yüzde yüze, dal kapsamını yüzde yetmiş beşe, yol kapsamını
  yüzde elliye çıkardı.
- Satır kapsamı her zaman dal kapsamından, dal kapsamı da yol kapsamından büyük ya da ona
  eşittir.
- Kapsanmayan yollardan biri gerçek bir hata barındırıyordu: zamanında iade edilen kitaba
  şube dışı ücreti uygulanıyordu.
- Düşük kapsam kesin bilgi verir, yüksek kapsam hiçbir şeyin kanıtı değildir; kapsam bir
  hedef olarak değil, sorulmamış soruların listesi olarak okunur.

## Sonraki Adım

Bu derste testler kodun içine bakmadan yazıldı: girdi verildi, dönüş değeri denetlendi.
Altıncı dersteki casus ve sahte nesneler ise kodun içine baktı — hangi çağrının yapıldığını
sınadılar. İki yaklaşım aynı davranışı doğrulasa da yeniden düzenleme karşısında aynı
davranmaz. Sonraki ders bir yeniden düzenleme gerçekten yapar ve iki test biçiminden
hangisinin kırıldığını koşumla gösterir.
