---
title: 'Kapsam Raporlama'
source: 'https://academia.sh/tr/kurslar/go-uretim/kapsam-raporlama'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aynı işlevi üç farklı test kümesiyle sınayıp kapsama oranını ve kapsanmayan blok sayısını koşumdan okur'
    - 'Kapsama oranının koşulan blokları saydığını, hangi iddiaların gerçekten çalıştığını saymadığını ölçer'
    - '%100 kapsamanın doğruluk göstermediğini, gerçek bir hatayı yakalamayan tam kapsamalı bir test kümesiyle sınırlar'
---

# Kapsam Raporlama

Aynı işlev üç ayrı test kümesiyle sınandığında kapsama oranı 57.1%, 85.7% ve 100.0% olarak üç ayrı değer alıyor; kapsanmayan blok sayısı da sırasıyla 3, 1 ve 0'a düşüyor. Sınırlayıcı ölçüm, %100 kapsama veren üçüncü kümenin, dönüş değerini hiç denetlemediği bir dalda gerçek bir hatayı hiç yakalamadığını hatalı bir kaynakla koşturarak gösteriyor.

Önceki ders bir programa dair on bir bilgiyi kaynak, araç ve koşum sınıflarına ayırdı;
bu on birin biri — kapsanan satır oranı — o derste yalnız tek bir sayı
(`80.0% of statements`) olarak geçti, ayrıntısına girilmedi. Bu son ders konunun kapanışı ve o
ayrıntıyı açıyor: kapsama oranı tam olarak neyi sayıyor ve bu sayı yükseldiğinde
gerçekte ne değişiyor?

Kapsama oranı üretim kod tabanlarında sık kullanılan bir gösterge: bir gözden geçirme,
bir eşik ("yeni kod en az şu oranda kapsanmalı") ya da bir panoda görünen tek bir yüzde.
Bu göstergenin ne söylediği ve ne söylemediği, bu konunun beş dersi boyunca kurulan
"bu bilgiyi kim söylüyor" sorusunun son sınavı — çünkü kapsama, tek başına
bakıldığında, bir programın sınandığını değil, yalnız çalıştırıldığını kanıtlıyor. Bu
soruyu yanıtlamak için konu boyunca kullanılan `envanter` paketine son bir kez dönülüyor;
bu kez paketin kendisi değil, aynı paketi sınayan üç farklı test kümesi konu.

## Aynı İşlev, Üç Test Kümesi, Üç Oran

`envanter` paketine son bir işlev ekleniyor: `Etiketle`, bir kalemin adedine bakıp dört
durumdan birini döndürüyor — negatif bir adet `"gecersiz"`, sıfır `"tukendi"`, üçün altı
`"az kaldi"`, geri kalanı `"yeterli"`. Dört durumun her biri kendi `return` satırında
ve dört durum birbirini dışlıyor: bir çağrı yalnız birine düşüyor.

Bu işlev bilinçli olarak dallanma sayısı ayarlanabilir bir örnek olarak kuruldu — dört
ayrı dal, dört ayrı test vakası ve her vakanın kapsamaya kaç ayrı blok eklediği tek tek
izlenebilir. `Gecerli` (Tablo Güdümlü Testler) ve `StokAzalt` (Sahteleme ve Arayüz
Ayrımı) da benzer dallı yapılar taşıyordu; bu ders aynı yapıyı, kapsama aracının bu
dallanmayı nasıl saydığını görmek için yeniden kullanıyor.

Üç test kümesi de aynı `envanterMetni` kaynağına karşı koşuyor — `Etiketle`'nin kendisi
hiç değişmiyor, değişen yalnız hangi durumların çağrıldığı. Bu, kapsama oranının bir
kaynağın özelliği değil, bir **kaynak-test çifti**nin özelliği olduğunu gösteriyor: aynı
kaynağın kapsama oranı, hangi test kümesiyle sorulduğuna göre 57.1% ile 100.0% arasında
herhangi bir değer alabiliyor.

`Kur` ve `Cagir` yordamları konunun ilk dersinden beri değişmedi; bu ders de her test
kümesi için tamamen boş bir geçici modül kuruyor, önceki bir koşumun izini taşımadan.

**TS18.** Aynı `Etiketle` işlevi, üç farklı test kümesiyle sınanıyor: `T1` yalnız
"yeterli" durumunu sınıyor, `T2` üç durumu ("yeterli", "tukendi", "az kaldi") sınıyor,
`T3` dördünü de çağırıyor.

```go
// araclar.go — kurulum, cagri ve kapsama okuma yordamlari, ve konu boyunca kullanilan Etiketle
package main

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func Kur(dosyalar map[string]string) string {
	dizin, _ := os.MkdirTemp("", "sinama")
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	for ad, icerik := range dosyalar {
		os.WriteFile(filepath.Join(dizin, ad), []byte(icerik), 0o644)
	}
	return dizin
}

func Cagir(dizin string, ekOrtam []string, arg ...string) (string, bool) {
	cmd := exec.Command("go", arg...)
	cmd.Dir = dizin
	cmd.Env = append(append(os.Environ(), "GOTOOLCHAIN=local"), ekOrtam...)
	cikti, err := cmd.CombinedOutput()
	return string(cikti), err == nil
}

func kapsamOrani(cikti string) string {
	for _, s := range strings.Split(cikti, "\n") {
		if i := strings.Index(s, "coverage: "); i >= 0 {
			return strings.TrimSpace(s[i+len("coverage: "):])
		}
	}
	return "yok"
}

// kapsanmayanBlok, kapsama profilindeki hic kosmamis blok sayisini sayar.
func kapsanmayanBlok(profil []byte) int {
	n := 0
	for _, satir := range strings.Split(string(profil), "\n")[1:] {
		alanlar := strings.Fields(satir)
		if len(alanlar) == 3 && alanlar[2] == "0" {
			n++
		}
	}
	return n
}

const envanterMetni = `package sinama

type Kalem struct {
	Ad    string
	Adet  int
	Fiyat int
}

func Etiketle(k Kalem) string {
	if k.Adet < 0 {
		return "gecersiz"
	}
	if k.Adet == 0 {
		return "tukendi"
	}
	if k.Adet < 3 {
		return "az kaldi"
	}
	return "yeterli"
}
`

const testMetniT3 = `package sinama

import "testing"

func TestEtiketle(t *testing.T) {
	if got := Etiketle(Kalem{"kalem", 5, 250}); got != "yeterli" {
		t.Errorf("got %s", got)
	}
	if got := Etiketle(Kalem{"kalem", 0, 250}); got != "tukendi" {
		t.Errorf("got %s", got)
	}
	if got := Etiketle(Kalem{"kalem", 2, 250}); got != "az kaldi" {
		t.Errorf("got %s", got)
	}
	Etiketle(Kalem{"kalem", -1, 250})
}
`
```

```go
// olcum.go — uc test kumesiyle uc ayri kapsama orani ve kapsanmayan blok sayisi
package main

import (
	"fmt"
	"os"
	"path/filepath"
)

const testMetniT1 = `package sinama

import "testing"

func TestEtiketleYeterli(t *testing.T) {
	if got := Etiketle(Kalem{"kalem", 5, 250}); got != "yeterli" {
		t.Errorf("got %s", got)
	}
}
`

const testMetniT2 = `package sinama

import "testing"

func TestEtiketle(t *testing.T) {
	if got := Etiketle(Kalem{"kalem", 5, 250}); got != "yeterli" {
		t.Errorf("got %s", got)
	}
	if got := Etiketle(Kalem{"kalem", 0, 250}); got != "tukendi" {
		t.Errorf("got %s", got)
	}
	if got := Etiketle(Kalem{"kalem", 2, 250}); got != "az kaldi" {
		t.Errorf("got %s", got)
	}
}
`

func main() {
	fmt.Println("-- kosum: uc test kumesiyle uc ayri oran --")
	kumeler := []struct{ ad, metin string }{
		{"T1", testMetniT1},
		{"T2", testMetniT2},
		{"T3", testMetniT3},
	}
	for _, k := range kumeler {
		dizin := Kur(map[string]string{"envanter.go": envanterMetni, "envanter_test.go": k.metin})
		cikti, _ := Cagir(dizin, nil, "test", "-coverprofile=kapsam.out", "./...")
		profil, _ := os.ReadFile(filepath.Join(dizin, "kapsam.out"))
		fmt.Println(k.ad, "kapsama orani:", kapsamOrani(cikti),
			"kapsanmayan blok sayisi:", kapsanmayanBlok(profil))
	}
}
```

```
-- kosum: uc test kumesiyle uc ayri oran --
T1 kapsama orani: 57.1% of statements kapsanmayan blok sayisi: 3
T2 kapsama orani: 85.7% of statements kapsanmayan blok sayisi: 1
T3 kapsama orani: 100.0% of statements kapsanmayan blok sayisi: 0
```

**TS19.** Vaka sayısı önceki iki derste de kaynaktan sayılabiliyordu; burada da sayılabilir
— `T1` bir çağrı, `T2` üç, `T3` dört. Ama kapsama oranının kendisi kaynaktan hiç
okunamıyor: aynı `Etiketle` işlevi değişmeden kalırken, yalnız hangi test kümesinin onu
çağırdığı değiştiğinde oran 57.1%'den 100.0%'e çıkıyor. `-coverprofile` bayrağıyla
üretilen profil dosyası bu oranın hangi **bloklardan** geldiğini de veriyor:
`kapsanmayanBlok`, profildeki her satırı okuyup son sütunu sıfır olanları sayıyor — bu
sütun, o bloğun koşum boyunca kaç kez çalıştığı. T1'de üç blok hiç çalışmıyor (üç
`return` satırı), T3'te hiçbiri.

Kapsama aracının kendisi bir koşum bilgisi üretiyor, çünkü hangi bloğun çalışıp
çalışmadığı ancak testler fiilen koşturulduktan sonra biliniyor — kaynağı okuyan biri
dört durumun var olduğunu görebilir, ama hangi test kümesinin bu dört durumdan kaçını
tetiklediğini kaynaktan çıkaramaz. Bu, Tablo Güdümlü Testler'de ölçülen ayrımın bu konudaki
son görünümü: bir sayı (vaka sayısı) kaynakta duruyor, bir başka sayı (kapsama oranı)
yalnız koşarak ortaya çıkıyor.

Üç kümenin de aynı yordamla — `Kur`, `Cagir` — koşturulduğuna dikkat etmek gerekiyor;
değişen yalnız `envanter_test.go` dosyasının içeriği. `Cagir` bu kez `-coverprofile`
bayrağıyla çağrılıyor ve bu bayrak `go test`'e yalnız bir oran değil, oranın dayandığı
ham veriyi de bir dosyaya yazmasını söylüyor. `-cover` bayrağı yalnız özet oranı
veriyordu (önceki derste kullanılan biçim); `-coverprofile` aynı bilgiyi blok blok
kaydediyor ve bu ders bu ayrıntıyı `kapsanmayanBlok` yordamıyla okuyor. Profil dosyasının
kendisi de bu programın çalışma dizininde geçici olarak duruyor ve hiçbir yerde yolu
basılmıyor — yalnız içeriğinden türetilen sayı okunuyor.

## Sınırlayıcı Ölçüm: Yüzde Yüz Kapsama Doğruluk Göstermiyor

`T3` dört durumun dördünü de çağırıyor ve son çağrısı —
`Etiketle(Kalem{"kalem", -1, 250})` — dönüş değerini hiç almıyor, hiç denetlemiyor. Bu satır kapsama aracının gözünde
tam bir sınama: `Etiketle` çağrıldı, ilgili blok çalıştı, oran %100'e çıktı. Ama bu
çağrının **doğru bir sonuç verip vermediği** hiçbir yerde sınanmadı.

Bunun bir yazım hatası olmadığını vurgulamak gerekiyor — `T3`'ün kaynağına bakan biri
dördüncü çağrıyı görüyor, `Etiketle`'nin dördüncü durumunun **çağrıldığını** doğruluyor.
Eksik olan, bir `if got := ...; got != "beklenen"` kalıbının o çağrıya da eklenmemiş
olması; üçüncü göze kadar bu kalıp hep vardı, dördüncüde düşürüldü. Kapsama aracı bu
düşüşü fark etmiyor çünkü onun sorduğu soru "bu satır çalıştı mı", "bu satırın sonucu bir
iddiaya bağlandı mı" değil.

Bu, Sahteleme ve Arayüz Ayrımı'nda ölçülen sınırla da bağlantılı: orada bir sahtenin
gerçeğin bütün varsayımlarını taşımadığı gösterilmişti, burada bir test kümesinin
kendisinin de kendi iddialarının bütün dalları kapsamadığı gösteriliyor — ikisi de aynı
kökten: bir sınamanın "geçmesi", sınamanın kapsamı dışında kalan hiçbir şey hakkında bir
güvence taşımıyor.

**TS20.** Aşağıdaki ölçüm bunu somutlaştırıyor: `Etiketle`'nin hatalı bir sürümü — negatif
adedi de `"tukendi"` diye etiketleyen, `"gecersiz"` durumunu hiç üretmeyen bir sürüm — aynı
`T3` test kümesiyle koşturuluyor.

```go
// sinir.go — T3 tam kapsamayla, hatali bir kaynakta ne diyor
package main

import (
	"fmt"
	"strings"
)

const envanterBozukMetni = `package sinama

type Kalem struct {
	Ad    string
	Adet  int
	Fiyat int
}

func Etiketle(k Kalem) string {
	if k.Adet < 0 {
		return "tukendi"
	}
	if k.Adet == 0 {
		return "tukendi"
	}
	if k.Adet < 3 {
		return "az kaldi"
	}
	return "yeterli"
}
`

func main() {
	fmt.Println("-- sinirlayici olcum: T3 tam kapsamayla, hatali kaynakta ne diyor --")
	dizin := Kur(map[string]string{"envanter.go": envanterBozukMetni, "envanter_test.go": testMetniT3})
	cikti, _ := Cagir(dizin, nil, "test", "-cover", "-v", "./...")
	gecti := strings.Contains(cikti, "--- PASS: TestEtiketle")
	fmt.Println("test gecti mi:", gecti, "kapsama orani:", kapsamOrani(cikti))
}
```

```
-- sinirlayici olcum: T3 tam kapsamayla, hatali kaynakta ne diyor --
test gecti mi: true kapsama orani: 100.0% of statements
```

**TS21.** Hatalı sürümde negatif bir adet artık `"gecersiz"` değil `"tukendi"`
döndürüyor — gerçek bir hata, çünkü bu iki durum `envanter` paketinin geri kalanında
farklı anlamlar taşıyabilir (stoğu biten bir kalemle stok sayımı bozulmuş bir kalem aynı
şey değil). Ama `T3` bu hatayı hiç yakalamıyor: test hâlâ geçiyor, kapsama oranı hâlâ
`100.0%`. Nedeni basit — `T3`'ün son satırı `Etiketle`'yi çağırıyor ama döndürdüğü
değerle hiçbir şey yapmıyor; kapsama aracı bu satırın **çalıştığını** görüyor, çalışan
satırın **doğru bir değer ürettiğini** hiç sormuyor.

İki koşumun (doğru kaynak, hatalı kaynak) kapsama oranı da bayt bayt aynı: ikisinde de
`100.0% of statements`. Kapsama aracı iki kaynak arasındaki farkı hiç görmüyor, çünkü
fark bir satırın çalışıp çalışmadığında değil, çalışan bir satırın hangi değeri
ürettiğinde. Bu, konunun her araç bilgisi için geçerli olan aynı sınırın kapsamaya
uygulanmış hâli: bir araç yalnız **kendi sorduğu soruyu** yanıtlıyor ve kapsama aracının
sorduğu soru hep aynı — bu satır çalıştı mı — hangi değeri ürettiği hiç sorulmuyor.

Bu, konunun altı dersi boyunca tekrar eden aynı sınırın son biçimi: Test Paketi
Temelleri'nde hiçbir şey sınamayan bir test geçiyordu, burada hiçbir şey sınamayan tek
bir **çağrı**, geri kalan üç iddiası sağlam bir test kümesinin kapsama oranını %100'e
taşıyor. Kapsama aracı **koşulan** blokları sayıyor, **doğru sınanan** blokları değil —
ve bu iki sayı, `T3` örneğinde olduğu gibi, aynı görünüp farklı bir şey söyleyebiliyor.
Yüzde yüz kapsamalı bir test kümesi, kaynağın her satırının en az bir kez çalıştığının
garantisi; kaynağın her satırının **doğru çalıştığının** garantisi değil.

Bunun üretim tarafındaki karşılığı somut: bir kod tabanında kapsama oranı %100'e
yaklaştıkça, ekleyen kişiler genellikle bu oranı bir tamamlanmışlık göstergesi olarak
okuyor. Yukarıdaki ölçüm bu okumanın eksik olduğunu gösteriyor — oran, satırların
çalıştığını söylüyor, o satırların **ürettiği değerlerin** doğrulandığını söylemiyor.
Bir kod tabanı yüzde yüz kapsamayla da hatalı davranabiliyor, çünkü kapsama aracının
kendisi bir iddianın var olup olmadığını hiç sormuyor — yalnız bir satırın çalışıp
çalışmadığını sayıyor. Bu, Test Paketi Temelleri'nde kurulan "geçen bir test doğru
olduğunu söylemez" iddiasının, kapsama oranına taşınmış hâli: yüksek bir kapsama oranı da
doğruluğu söylemiyor, yalnız neyin çalıştırıldığını söylüyor.

Bu son ölçüm, kapsamanın kendisinin de bir gösterge olduğunu, hedef olmadığını
gösteriyor: %100'e ulaşmak kolay, ulaşırken hangi soruların hâlâ yanıtsız kaldığını
görmek zor. Kapsama aracı bu zorluğu çözmüyor, yalnız hangi satırların hiç
çalıştırılmadığını gösteriyor — geri kalanı, bir iddianın gerçekten o satırın ürettiği
değeri denetleyip denetlemediği, yazan kişinin elinde kalıyor.

## Özet

- Aynı `Etiketle` işlevi üç test kümesiyle sınandığında kapsama oranı üç farklı değer
  aldı (57.1%, 85.7%, 100.0%); vaka sayısı kaynaktan sayılabildi, kapsama oranı yalnız
  koşarak öğrenilebildi.
- Kapsama profili hangi bloğun hiç çalışmadığını da veriyor; kapsanmayan blok sayısı test
  kümesi genişledikçe 3'ten 0'a düştü.
- Sınırlayıcı ölçüm: dört durumu da çağıran ama son çağrısının sonucunu denetlemeyen bir
  test kümesi, hatalı bir kaynakla koşturulduğunda hâlâ geçti ve hâlâ %100 kapsama verdi.
- Kapsama aracı koşulan satırları sayıyor, doğru sınandığını söylemiyor — bu ayrım
  konunun altı dersinde tekrar eden aynı sınırın son görünümü.
- Sahte bağımlılık ailesi, tablo güdümlü test kalıbı ve kapsama ölçümü Birim Testi ve
  Test Odaklı Geliştirme kursunda kuruldu; bu ders yalnız Go'nun kendi kapsama aracını
  ölçtü.

## Sonraki Adım

Bu konu boyunca aynı soru altı kez soruldu: bir testin kararı, bir vaka listesi, bir HTTP
işleyicisinin yanıtı, bir sahte bağımlılığın sınırı, karşılaştırmalı ölçümün hangi sayısı
tekrarlanabilir ve şimdi kapsama oranının neyi saydığı — hepsi kaynak, araç ya da koşum
etiketiyle sınıflandı ve altı dersin her biri kendi sınırlayıcı ölçümünü koşturarak
gösterdi: geçen bir test doğru olduğunu söylemiyor, vaka eklemek kapsamayı artırmıyor,
test yardımcısı ağı değil işleyiciyi sınıyor, sahte gerçekleştirme gerçeğin bütün
varsayımlarını taşımıyor, ayırma sayısı da neyi saydığını söylemeden karşılaştırılamıyor
ve şimdi kapsama koşulan satırları sayar, doğru sınandığını söylemez. Sıradaki konu aynı
soruyu belleğe soruyor: bir profil de tıpkı kapsama gibi koşan yolu görüyor ve nerede
bellek ayrıldığını söylüyor — ama neden yavaş olduğunu söylemiyor.
