---
title: 'Test ve Özellik Tabanlı Sınama'
source: 'https://academia.sh/tr/kurslar/rust-ileri/test-ve-ozellik-tabanli-sinama'
course: 'Rust Ekosistemi ve İleri Konular'
language: tr
updated: '2026-09-13T05:09:27+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Sabit tohumlu, belirlenimci bir üreteçle üretilen girdilerde bir özelliği sınar ve tutan girdi sayısını sayar'
    - 'Ters çevirmenin iki kez uygulanmasının özdeşlik verdiğini iki yüz girdide doğrulayarak güvencenin sınıfını (devredildi) gösterir'
    - 'Geçen bir özellik sınamasının her girdi için doğru anlamına gelmediğini, yalnızca bazı girdilerde tutan bir özellikle açıklar'
---

# Test ve Özellik Tabanlı Sınama

Sabit tohumlu, belirlenimci bir üreteçle üretilen iki yüz girdide bir özelliğin tutup tutmadığının sayılması ve bu sayının, derleyicinin hiç sormadığı bir soruyu hangi sınırla yanıtladığının gösterilmesi.

Bağımlılık ve Özellik Bayrakları dersi, derleyicinin hiç görmediği bir gövdeyi
ölçtü — orada güvence hiç başlamıyordu. Ama önceki dersin son sorusu farklı bir
yöne işaret ediyordu: `calisan_govde` gibi bir gövde derlemeye giriyor, tip
denetiminden geçiyor, adları doğru çözülüyor ve yine de **yanlış** bir sonuç
üretebiliyor. Derleyici bir gövdenin var olduğunu, tipinin tutarlı olduğunu
denetler; o gövdenin *doğru* olduğunu hiçbir zaman denetlemez — doğruluk tipin
değil, davranışın bir özelliğidir ve tip denetimi davranışı bilmez.

Bu boşluğu kapatmanın bir yolu koşarak sınamaktır: bir gövdeyi belirli girdilerle
çalıştırıp sonucun beklenen bir özelliği taşıyıp taşımadığına bakmak. Bu, tek bir
sabit girdiyle örnek sınaması yazmaktan farklıdır — bir örnek sınaması yalnız o
tek girdide bir şeyin doğru olduğunu gösterir, seçilmemiş hiçbir girdi hakkında
konuşmaz. Bu ders bunu tek bir sabit girdiyle değil, sabit tohumlu bir üreteçle
üretilen çok sayıda girdiyle yapıyor — özellik tabanlı sınamanın temel fikri budur:
tek bir örnek yerine, aynı özelliği birçok üretilmiş örnekte sınamak. Girdilerin belirlenimci bir
üreteçten gelmesi bir yan koşul değil, bu dersin kurduğu gözlemin gerekli
şartlarından biri: sistemin saatine ya da işletim sisteminin rastgelelik
kaynağına dayanan bir üreteç kullanılsaydı, aynı sınama her makinede ve her
koşumda farklı girdilerle çalışırdı ve tutan girdi sayısı koşumdan koşuma değişirdi
— bu ders o değişkenliği baştan dışlıyor. Sınamanın
kendisi denetlenimsiz değildir: hangi özelliğin sınanacağına, girdilerin hangi
aralıktan geleceğine ve kaç girdinin yeteceğine karar veren, derleyici değil,
**sınamayı yazan taraftır**. Kursun ortak sorusu — derleyicinin güvencesi burada
nerede bitiyor — bu derste yanıtını buluyor: tip denetiminin bittiği yerde,
davranış denetimi başlıyor, ama bu denetimin kapsamını da yazan kişi çiziyor.

## Özelliğin Koşarak Sınanması

Bir özellik tabanlı sınama üç parçadan kurulur: rastgele girdi üreten bir kaynak, bu
girdilerin her birinde sınanacak bir koşul ve koşulun kaç girdide tuttuğunu sayan bir
döngü. Rust'ın standart kütüphanesinde rastgele sayı üreteci yoktur ve bu müfredatta
üçüncü taraf kasa yasaktır; bu yüzden üreteç elle yazılır. **PY5.** Rastgele olması
gereken tek şey girdilerin *dağılımıdır* — sınamanın kendisi belirlenimci kalmalıdır,
aksi hâlde aynı koşum farklı makinelerde farklı sonuç verir. Aşağıdaki üreteç, sabit
bir tohumdan başlayan ve her çağrıda aynı sırayı üreten bir doğrusal uyumlu üreteçtir
(linear congruential generator): tohum aynıysa çıktı dizisi de her koşumda birebir
aynıdır.

Üretecin durumu tek bir `u64` sayıdır ve her çağrıda sabit iki sayıyla çarpılıp
toplanarak güncellenir; `wrapping_mul` ve `wrapping_add` bu işlemde taşmayı sessizce
sarmalar, böylece işlem hiçbir zaman panikle durmaz. `uzunluk` bu ham sayıyı bir
üst sınıra göre daraltır (bir dizinin kaç eleman taşıyacağını belirler), `eleman` ise
`-500` ile `499` arasında bir tam sayı üretir. `vektor` bu ikisini birleştirir: önce
bir uzunluk çeker, sonra o uzunlukta eleman üretir. Kullanılan sabitler bilinen bir
doğrusal uyumlu üreteç ailesinden alınmıştır ve üretecin *iyi dağılması* bu dersin
konusu değildir — burada aranan tek özellik, aynı tohumla her koşumda aynı diziyi
üretmesidir; bu özellik yukarıdaki tüm koşumlarda birebir aynı sayıları verdi.

```rust
// dogrusal_uretec.rs — sabit tohumlu, belirlenimci sozde-rastgele uretec
pub struct DogrusalUretec {
    durum: u64,
}

impl DogrusalUretec {
    pub fn yeni(tohum: u64) -> Self {
        DogrusalUretec { durum: tohum }
    }

    pub fn sonraki(&mut self) -> u64 {
        self.durum = self
            .durum
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        self.durum
    }

    pub fn uzunluk(&mut self, ust_sinir: usize) -> usize {
        (self.sonraki() % (ust_sinir as u64 + 1)) as usize
    }

    pub fn eleman(&mut self) -> i32 {
        (self.sonraki() % 1000) as i32 - 500
    }

    pub fn vektor(&mut self, en_uzun: usize) -> Vec<i32> {
        let n = self.uzunluk(en_uzun);
        (0..n).map(|_| self.eleman()).collect()
    }
}
```

Sınanacak özellik, Proje Yaşam Döngüsü konusunun brifinginde de anılan klasik bir
örnek: bir dizinin ters çevrilmesi iki kez uygulanınca özgün dizi geri gelmeli. Bu
özellik `Vec`in `reverse` yöntemiyle kurulur ve elle üretilen her girdi için sınanır.
İki yüz girdi üretilir, her biri en çok yirmi elemanlı; kaç girdide özelliğin tuttuğu
sayılır.

```rust
// ozellik_sinamasi.rs — ters cevirmenin iki kez uygulanmasi ozdeslik verir mi
mod dogrusal_uretec;
use dogrusal_uretec::DogrusalUretec;

fn ters_ters_ozdeslik(v: &[i32]) -> bool {
    let mut once = v.to_vec();
    once.reverse();
    once.reverse();
    once == v
}

fn main() {
    let mut uretec = DogrusalUretec::yeni(42);
    let toplam_girdi = 200;
    let mut tutan = 0;

    for _ in 0..toplam_girdi {
        let girdi = uretec.vektor(20);
        if ters_ters_ozdeslik(&girdi) {
            tutan += 1;
        }
    }

    println!("sinanan girdi sayisi: {}", toplam_girdi);
    println!("ozelligin tuttugu girdi sayisi: {}", tutan);
}
```

```
sinanan girdi sayisi: 200
ozelligin tuttugu girdi sayisi: 200
```

Üreteç tohum `42` ile başladığında ürettiği ilk dizi on dört elemanlıdır, ikincisi on
bir, üçüncüsü on sekiz — uzunluklar da tıpkı elemanlar gibi sabit tohumla
belirlenimci biçimde değişir. Bu koşumda yazdırılan yalnızca toplam ve tutan
sayılar, ama üretecin ürettiği iki yüz dizinin her biri bu şekilde farklı
uzunluklarda ve farklı elemanlarla geliyor; `vektor`a verilen üst sınır (`20`) bir
dizinin en çok yirmi eleman taşıyabileceğini, en az sıfır (boş dizi) olabileceğini
belirtiyor. En küçük uzunluk sıfır bırakıldı çünkü boş dizi de geçerli bir girdi —
ters çevirmenin iki kez uygulanması boş bir dizide de özdeşlik vermelidir ve üreteç
bu uç durumu da zaman zaman üretiyor.

Ters çevirmenin iki kez uygulanması, özellik tabanlı sınamanın anlatımını
kolaylaştırmak için seçilmiş küçük bir örnek; aynı yaklaşım daha büyük gövdelere de
uygulanabilir. Bir çözümleyicinin (parser) ürettiği bir yapıyı yeniden dizgeye
çevirip tekrar çözümlemek, ilk yapıyla aynı sonucu vermeli; bir sıralama işlevinin
çıktısı, girdiyle aynı elemanları taşımalı ve komşu elemanları sıralı olmalı. Bu
derste seçilen özellik en basitlerinden biri — bir işlemin kendi tersiyle
birleştiğinde özdeşlik vermesi — ama ölçme biçimi (üreteç, özellik, sayaç) aynı
kalıyor: hangi gövde sınanırsa sınansın, derleyicinin denetlemediği yeri koşarak
kapatmak, hep aynı üç parçayı gerektiriyor.

İki yüz girdinin ikisi yüzü de özelliği doğruladı. **PY6.** Bu sayı üreteçle
birlikte belirlenimcidir: tohum `42` olduğu sürece üretilen iki yüz dizinin her biri
aynı sırayla, aynı uzunlukta ve aynı elemanlarla gelir; koşum kaç kez tekrarlanırsa
tekrarlansın sonuç değişmez. Bu, "rastgele" sözcüğünün bu derste ne anlama geldiğini
de netleştiriyor: üretilen girdiler *çeşitlidir* (elle tek tek yazılmamıştır), ama
üretim süreci *rastgele değildir* — her koşumda aynı diziyi üretir.

**Ayrım cümlesi:** M09/K05 kursunun Kapsam Raporlama dersi bir sınamanın **hangi
satırları** çalıştırdığını ölçtü — kapsama oranı, kaynağın ne kadarının koşumdan
geçtiğini gösteriyordu. Bu ders satırları saymıyor; bir özelliğin **kaç girdide
tuttuğunu** sayıyor. Bir satırın çalışması onun doğru çalıştığı anlamına gelmez —
yalnızca çalıştığı anlamına gelir; bu derste ölçülen, çalışmanın doğruluğa dair
verdiği güvencenin gücü.

Güvencenin sınıfı burada **devredildi**: derleyici `ters_ters_ozdeslik` işlevinin
tipini denetledi ve durdu, işlevin *her girdi için* doğru sonuç verip vermediğini
hiç sormadı. Bu soruyu soran ve yanıtlayan, sınamayı yazan taraf oldu — hangi
özelliğin sınanacağına, girdilerin hangi aralıktan geleceğine (`-500`'den `499`'a,
en çok yirmi elemanlı) ve kaç girdinin (iki yüz) yeteceğine yazan karar verdi.
Derleyici bu kararların hiçbirine karışmadı; yalnızca yazılan sınama kodunun
kendisinin tipçe tutarlı olduğunu denetledi.

**Devredildi** sınıfı, `unsafe` Rust dersinde ölçülen devirle aynı sözcüğü taşır ama
farklı bir yerde duruyor: orada devredilen, bir belleğe erişimin sınır içinde
kaldığı sözleşmesiydi; burada devredilen, bir davranışın *doğru* olduğu iddiası.
İkisinde de ortak olan, derleyicinin bir noktada durup geri kalan sorumluluğu
kaynağı yazan tarafa bırakmasıdır. Bu devrin bir sonucu da şu: `ters_ters_ozdeslik`
işlevinin kendisi yanlış yazılmış olsaydı — örneğin ikinci `reverse` çağrısı
unutulsaydı — derleyici bunu da fark etmezdi, çünkü yanlış yazılmış bir sınama da
tıpkı doğru yazılmış biri gibi tip denetiminden geçer. Sınamanın doğruluğu da,
sınadığı gövdenin doğruluğu gibi, yazan tarafın sorumluluğunda kalır.

Girdi sayısının kaç olacağı da aynı devrin bir parçası. Bu derste iki yüz seçildi;
elli seçilseydi sınama daha az girdi görürdü, iki bin seçilseydi daha çok. Sayı büyüdükçe
kapsanan girdi kümesi genişler, ama hiçbir sayı **bütün** girdileri kapsamaz. Derleyici bu sayı hakkında hiçbir görüş bildirmez —
sayı ne kadar küçük olursa olsun ya da ne kadar büyük olursa olsun, sınama kodu
aynı biçimde tip denetiminden geçer ve derlenir. Sayının yeterliliğine karar vermek,
üretecin aralığını seçmeye karar vermekle aynı sorumluluğun parçası: her ikisi de
yazan tarafın elinde, derleyicinin denetim alanının tamamen dışında.

## Sınırlayıcı Ölçüm

Yukarıdaki koşum, seçilen özelliğin **her** girdide tuttuğunu gösterdi — ama bu
sonuç, seçilen özelliğin doğru bir matematiksel gerçek olmasından kaynaklanıyor:
bir diziyi iki kez ters çevirmek, sıralamadan bağımsız olarak her zaman özgün diziyi
verir. Bütün özellikler böyle değildir. Aşağıdaki koşum, bazı girdilerde tutan ama
bazılarında tutmayan bir özelliği aynı üreteçle sınıyor: bir dizinin kendi tersiyle
aynı olması, yani bir bakışımlılık (palindrome) koşulu.

```rust
// sinir_sinamasi.rs — bazi girdilerde tutan, bazilarinda tutmayan bir ozellik
mod dogrusal_uretec;
use dogrusal_uretec::DogrusalUretec;

fn kendi_tersiyle_ayni(v: &[i32]) -> bool {
    let mut ters = v.to_vec();
    ters.reverse();
    ters == v
}

fn main() {
    let mut uretec = DogrusalUretec::yeni(7);
    let toplam_girdi = 200;
    let mut tutan = 0;
    let mut ilk_tutmayan: Option<Vec<i32>> = None;

    for _ in 0..toplam_girdi {
        let girdi = uretec.vektor(6);
        if kendi_tersiyle_ayni(&girdi) {
            tutan += 1;
        } else if ilk_tutmayan.is_none() {
            ilk_tutmayan = Some(girdi);
        }
    }

    println!("sinanan girdi sayisi: {}", toplam_girdi);
    println!("ozelligin tuttugu girdi sayisi: {}", tutan);
    println!("tutmayan ilk girdi uzunlugu: {}", ilk_tutmayan.map(|v| v.len()).unwrap_or(0));
}
```

```
sinanan girdi sayisi: 200
ozelligin tuttugu girdi sayisi: 62
tutmayan ilk girdi uzunlugu: 3
```

**PY7.** İki yüz girdinin altmış ikisinde özellik tuttu, geri kalan yüz otuz
sekizinde tutmadı — üreteç kısa diziler de üretebildiği için (en çok altı eleman,
sıfır dâhil), bazı girdiler tesadüfen bakışımlı çıktı: boş dizi ve tek elemanlı dizi
her zaman kendi tersiyle aynıdır, bu da tutan sayıyı yükseltiyor. Tutmayan ilk
girdinin uzunluğu üç — üç elemanlı bir dizinin bakışımlı olması, elemanların
kendisine bağlı, uzunluğuna değil.

Bu ölçüm, geçen bir özellik sınamasının ne anlama geldiğini sınırlıyor. Önceki
koşumda özellik iki yüz girdinin ikisi yüzünde de tuttu, çünkü sınanan özellik
*her zaman* doğru bir matematiksel gerçekti; bu koşumda özellik yalnız altmış iki
girdide tuttu, çünkü sınanan özellik *bazen* doğru. İkisi arasındaki farkı sınamanın
kendisi göstermiyor — ikisi de "geçen sınama" olarak görünüyor, geçen girdi
sayısının tümüyle eşit olması dışında. Bir özellik sınamasının geçmesi, o özelliğin
sınanan girdi kümesi için tuttuğunu kanıtlar; sınanmayan bir girdi için hiçbir şey
söylemez. İki yüz girdi geniş bir örneklem olabilir, ama üreteç sınırlı bir aralıktan
(`-500` ile `500` arası, en çok yirmi eleman) çekim yapıyor — bu aralığın dışındaki
ya da bu uzunluğun ötesindeki bir girdi hiç sınanmadı. **PY8.** Geçen bir sınamanın
verdiği güvence, sınadığı girdi sayısı ve aralığıyla sınırlıdır; iddia bu sayıya
bağlanmadan yazıldığında güvencenin gerçek kapsamı gizlenmiş olur.

Ölçümün taşıdığı bir sınır daha var: tutmayan ilk girdi yalnız uzunluğuyla
bildirildi, elemanlarıyla değil — bu, ortama bağlı olmayan bir seçim değil, bilinçli
bir seçim. Gerçek özellik tabanlı sınama araçları, tutmayan bir girdi bulunca onu
*küçültme* (shrinking) adı verilen bir süreçle daha küçük, daha okunabilir bir
karşı örneğe indirger; bu ders böyle bir küçültme adımı yazmıyor, yalnız ilk
tutmayan girdinin uzunluğunu bildiriyor. Bu, ölçümün bir başka sınırı: bir özelliğin
tutmadığını göstermek, en *anlaşılır* karşı örneği bulmakla aynı şey değil — bu
derste sayılan yalnız kaç girdide tutup tutmadığı, hangi girdinin en açıklayıcı
karşı örnek olduğu değil.

Altmış iki sayısının kendisi de bir sınır taşıyor: bu, tohum `7` ile üretilen bu
belirli iki yüz girdinin verdiği sayı. Tohum `11` ya da `100` seçilseydi, aynı
özellik aynı aralıkta sınansa bile tutan girdi sayısı farklı çıkardı — çünkü üretilen
girdi kümesi tohuma bağlı olarak değişir. Bu ders bu değişkenliği ölçmüyor; tek bir
tohumla, tek bir koşumu belirlenimci kılıp o koşumun sonucunu bildiriyor.
Belirlenimcilik burada tekrarlanabilirliği garanti ediyor (aynı tohum her zaman
aynı altmış ikiyi verir), ama sınanan aralığın *temsil gücünü* garanti etmiyor —
o güç, aralığın ve üretecin nasıl tasarlandığına bağlı, tek bir koşumun sonucuna
değil.

## Özet

- Rust'ın standart kütüphanesinde rastgele sayı üreteci yoktur; bu derste sabit
  tohumlu, elle yazılmış bir doğrusal uyumlu üreteç kullanıldı — aynı tohum her
  koşumda aynı girdi dizisini üretir.
- Ters çevirmenin iki kez uygulanmasının özdeşlik verdiği özelliği, iki yüz
  üretilmiş girdinin ikisi yüzünde de tuttu.
- Güvencenin sınıfı **devredildi**: derleyici sınama kodunun tipini denetledi ve
  durdu; hangi özelliğin, hangi girdi aralığında, kaç girdiyle sınanacağına yazan
  taraf karar verdi.
- Bazen doğru olan bir özellik (kendi tersiyle aynı olma), aynı üreteçle sınandığında
  iki yüz girdinin yalnız altmış ikisinde tuttu — geçen bir sınama her girdi için
  doğru anlamına gelmiyor.
- Bu ölçümün sınırı: bir özellik sınamasının verdiği güvence yalnızca sınanan girdi
  sayısı ve aralığı kadardır; aralık dışındaki bir girdi hiç sınanmamıştır.
- Girdi sayısı, aralığı ve sınanan özelliğin kendisi — hangisinin ne kadar
  yeteceği de dâhil — derleyicinin değil, sınamayı yazan tarafın kararıdır; aynı
  üç parça (üreteç, özellik, sayaç) ters çevirme dışındaki gövdelere de uygulanır.

## Sonraki Adım

Bu ders, bir özelliğin doğru çalıştığını *koşarak* gösterebileceğimizi, ama bu
gösterimin sınırlı sayıda girdiyle ve yazan tarafın kararlarıyla sınırlı kaldığını
ölçtü. Sıradaki ders aynı "koşarak kurulan güvence" fikrini bambaşka bir soruya
uyguluyor: iki yazımdan hangisinin daha az iş yaptığı da yalnız koşularak
öğrenilebilir, doğruluk gibi derleyicinin hiç sormadığı bir soru. Ama bu kez
sınanan doğruluk değil, işlem sayısı; ve girdi sayısı yerine bu kez bir tek girdide
kaç karşılaştırma, kaç kopyalama yapıldığı sayılıyor. Karşılaştırmalı Ölçüm ve
Profilleme dersi, bu sayının neyi *söyleyip* neyi söylemediğini — özellikle neden bir
süre değil — inceliyor.
