---
title: 'Test Kararlılığı'
source: 'https://academia.sh/tr/kurslar/frontend-kalitesi/test-kararliligi'
course: 'Frontend Kalitesi'
language: tr
updated: '2026-08-17T18:09:22+00:00'
license: 'CC BY-SA 4.0'
---

# Test Kararlılığı

Kırılgan testin tanımı ve maliyeti, zamanlama, sıraya bağımlılık ve paylaşılan durumun belirlenimci bir benzetimle sayılması, her nedenin çözümü ve yeniden deneme ile karantinanın yeri.

Önceki dört dersin testleri bir varsayıma dayanıyordu: aynı kod aynı sonucu verir. Bu
varsayım sık sık bozulur. Bir test yirmi koşumun on dokuzunda geçer, birinde düşer; kimse
bir şey değiştirmemiştir. İkinci çalıştırmada yeşile döner ve iş devam eder.

Bu davranış zararsız görünür ama paketin verdiği işareti aşındırır. Kırmızı bir sonucun
anlamı belirsizleştiğinde ekip önce yeniden çalıştırmayı dener, sonra bakmadan yeniden
çalıştırır, en sonunda gerçek bir kusuru da kırılganlık sanıp geçer. Test paketinin bütün
değeri, kırmızının bir şey ifade etmesindedir.

## Kırılgan Test ve Ölçüsü

**Kırılgan test (flaky test)**, sınadığı kod değişmediği hâlde koşumdan koşuma farklı sonuç
veren testtir. Kusur testin kendisindedir: testin sonucu, sınadığı davranışın dışındaki bir
şeye bağlıdır.

Bu bir izlenim olarak değil, sayı olarak ele alınmalıdır. **Düşme oranı**, bir testin
belirli sayıda koşumda kaç kez düştüğüdür. Bir test bilinmeyen aralıklarla düşüyorsa, onu
aynı koşullarda çok kez çalıştırıp oranı ölçmek hem sorunun varlığını hem de düzeltmenin
işe yaradığını kanıtlar.

## Üç Nedeni Saymak

Aşağıdaki benzetim üç kırılganlık nedenini ayrı ayrı modelliyor ve sözde rastgele ama
belirlenimci bir üreteçle iki yüz koşum boyunca kaç kez düştüklerini sayıyor. Üreteç
doğrusal eşlenik yöntemidir: aynı tohum her zaman aynı diziyi verir, dolayısıyla sonuçlar
yeniden üretilebilir.

```js
// kirilganlik.mjs — uc kirilganlik nedeninin belirlenimci benzetimi
export function uretec(tohum) {
  let s = tohum >>> 0;
  return () => {
    s = (Math.imul(s, 1664525) + 1013904223) >>> 0;
    return s / 4294967296;
  };
}

function karistir(dizi, rastgele) {
  const k = [...dizi];
  for (let i = k.length - 1; i > 0; i -= 1) {
    const j = Math.floor(rastgele() * (i + 1));
    [k[i], k[j]] = [k[j], k[i]];
  }
  return k;
}

export function paketiKosur({ tohum = 7, kosum = 200, duzeltilmis = false, karistirma = true }) {
  const rastgele = uretec(tohum);
  const adlar = ['tabloYuklenir', 'suzgecDaraltir', 'formGonderilir'];
  const dusen = { tabloYuklenir: 0, suzgecDaraltir: 0, formGonderilir: 0 };
  let taslak = null; // modul duzeyinde yasayan paylasilan durum

  for (let n = 0; n < kosum; n += 1) {
    const sira = karistirma ? karistir(adlar, rastgele) : adlar;
    const kurulan = new Set();
    if (duzeltilmis) taslak = null; // her kosum kendi durumuyla baslar

    for (const ad of sira) {
      let gecti;
      if (ad === 'tabloYuklenir') {
        const yanitSuresi = 40 + 80 * rastgele();
        gecti = duzeltilmis ? yanitSuresi <= 500 : yanitSuresi <= 100;
        kurulan.add('tablo');
      } else if (ad === 'suzgecDaraltir') {
        if (duzeltilmis) kurulan.add('tablo'); // test kendi verisini kurar
        gecti = kurulan.has('tablo');
      } else {
        gecti = taslak === null; // form bos taslakla acilmali
        const gonderimBasarili = rastgele() < 0.5;
        taslak = gonderimBasarili ? null : { istasyon: 'kuzey-yamac-01' };
      }
      if (gecti === false) dusen[ad] += 1;
    }
  }
  return dusen;
}
```

```js
// rapor.mjs — 200 kosumda hangi test kac kez dustu
import { paketiKosur } from './kirilganlik.mjs';

const senaryolar = [
  ['sabit sira, duzeltilmemis', { karistirma: false }],
  ['karisik sira, duzeltilmemis', { karistirma: true }],
  ['karisik sira, duzeltilmis', { karistirma: true, duzeltilmis: true }],
];

console.log('senaryo                       tabloYuklenir  suzgecDaraltir  formGonderilir');
for (const [ad, secenek] of senaryolar) {
  const d = paketiKosur({ tohum: 7, kosum: 200, ...secenek });
  console.log(
    ad.padEnd(30) +
      String(d.tabloYuklenir).padStart(9) +
      String(d.suzgecDaraltir).padStart(16) +
      String(d.formGonderilir).padStart(16),
  );
}
```

```sh
node rapor.mjs
```

```
senaryo                       tabloYuklenir  suzgecDaraltir  formGonderilir
sabit sira, duzeltilmemis            50               0              95
karisik sira, duzeltilmemis          48             102             104
karisik sira, duzeltilmis             0               0               0
```

Üç satır, üç ayrı ders veriyor.

## Zamanlama

`tabloYuklenir` testi listenin gelmesi için sabit yüz milisaniye bekliyor. Benzetimde yanıt
süresi kırk ile yüz yirmi milisaniye arasında değişiyor; süre beklemeyi aştığında test
düşüyor. İki yüz koşumun ellisinde ve kırk sekizinde düştü — dörtte bire yakın bir oran,
koşum sırasından bağımsız.

Sabit bekleme iki yönden yanlıştır. Yavaş bir koşumda yetmez; hızlı bir koşumda boşa
harcanır. Yüz test için eklenen yüzer milisaniye, paketin süresine on saniye ekler ve hiçbir
şeyi güvence altına almaz.

Doğru biçim **koşula göre beklemedir**: bir gözlemlenebilir sonuç oluşana kadar kısa
aralıklarla denetleyip beklemek, cömert bir üst sınırla. Düzeltilmiş sürümde üst sınır beş
yüz milisaniyedir ve iki yüz koşumun hiçbirinde aşılmadı. Üst sınırın cömert olması testi
yavaşlatmaz — koşul erken sağlandığında bekleme erken biter. Üst sınırın büsbütün
kaldırılması ise kabul edilmez: gerçekten donmuş bir uygulamada test sonsuza kadar bekler.

Eşzamansız JavaScript kursundaki mikro görev sırası burada da geçerlidir. Bir sözün
sonuçlanması ile ekranın güncellenmesi aynı tur içinde olmayabilir; beklenecek şey sözün
kendisi değil, kullanıcının göreceği sonuçtur.

## Sıraya Bağımlılık

`suzgecDaraltir` testi, tablonun daha önce yüklenmiş olmasını bekliyor — veriyi kuran başka
bir testtir. Birinci satırda bu test hiç düşmedi: sabit sırada `tabloYuklenir` her zaman
önce çalışıyor. İkinci satırda sıra karıştırıldı ve iki yüz koşumun yüz ikisinde düştü.

Buradaki asıl bulgu şudur: **sabit sıra kusuru gizler.** Test paketi aylarca yeşil kalabilir,
sonra bir dosya adı değiştiğinde ya da koşucu paralelleştiğinde birden kırmızıya döner.
Sırayı bilerek karıştırmak bir tanı aracıdır; karıştırmada kullanılan tohum çıktıya
yazıldığında, düşen sıralama birebir yeniden üretilebilir.

Çözüm sırayı düzeltmek değil, bağımlılığı kaldırmaktır. Her test ihtiyaç duyduğu veriyi
kendisi kurar. Düzeltilmiş sürümde `suzgecDaraltir` kendi ölçümünü ekliyor ve sıradan
bağımsız hâle geliyor.

## Paylaşılan Durum

`formGonderilir` testi formun boş bir taslakla açılmasını bekliyor. Taslak modül düzeyinde
yaşıyor ve yalnız gönderim başarılı olduğunda temizleniyor; başarısız gönderimde bir sonraki
koşuma kalıyor. İki yüz koşumun doksan beşinde ve yüz dördünde düştü.

Bu neden diğer ikisinden bir yönüyle ayrılır: test kendi başına, temiz bir ortamda her zaman
geçer. Düşmesi için önce bir koşumun ortamı kirletmiş olması gerekir. Geliştirici makinesinde
tek başına çalıştırıldığında geçen, paket içinde düşen test tam olarak budur — ve tanıyı en
çok zorlaştıran davranış budur, çünkü düşen testin kendisinde bir kusur yoktur.

Paylaşılan durumun kaynakları sayılabilir: modül düzeyinde tutulan değerler, yerel depolama
ve oturum depolaması, çerezler, program denetimli önbellek, arka uçtaki veri ve dosya
sistemi. Çözüm her testten önce kurulan ve sonra yıkılan bir düzenektir. Düzeltilmiş sürümde
taslak her koşumun başında sıfırlanıyor ve düşme sıfıra iniyor.

Paralel koşum bu sorunu büyütür. Aynı arka uç kaydını ya da aynı bağlantı noktasını
paylaşan iki koşucu birbirinin ortamını bozar; yalıtım, koşum başına ayrı veri kümesiyle
sağlanır.

## Kırılganlıkla Ne Yapılır

Yeniden deneme akla ilk gelen çözümdür ve tek başına yanlıştır. Düşen testi sessizce yeniden
çalıştırmak kırılganlığı gizler; gizlenen kırılganlık zamanla artar ve arasına karışan
gerçek kusurlar da gizlenir. Kabul edilebilir tek biçim, yeniden denemenin **kaydedilmesi**
ve düşme oranının bir ölçü olarak izlenmesidir.

Bir test düzeltilemeyecek kadar karmaşıksa **karantinaya** alınır: paketten ayrılır, sonucu
yayımı engellemez, ama listede bir sahibi ve bir son tarihi vardır. Sahipsiz karantina,
testin sessizce silinmesinin uzun yoludur.

Belirlenimciliği artıran genel önlemler de vardır. Saat sabitlenir, yerel ayar ve saat
dilimi açıkça verilir, rastgelelik bu dersteki gibi tohumlanır ve tohum çıktıya yazılır.
Amaç aynıdır: testin sonucunu, sınadığı davranışın dışındaki hiçbir şeye bağlı bırakmamak.

## Özet

- Kırılgan test, sınadığı kod değişmediği hâlde koşumdan koşuma farklı sonuç veren testtir;
  maliyeti kırmızı sonucun anlamını yitirmesidir.
- Sabit bekleme yanıt süresi dağılımının kuyruğunda düşer; koşula göre bekleme cömert bir
  üst sınırla bu nedeni sıfırladı.
- Sabit koşum sırası sıraya bağımlılığı gizler: aynı test sabit sırada hiç düşmezken karışık
  sırada iki yüz koşumun yüz ikisinde düştü.
- Paylaşılan durumun imzası, testin yalnız başına geçip paket içinde düşmesidir; her testin
  kendi düzeneğini kurup yıkması bu nedeni sıfırladı.
- Yeniden deneme kırılganlığı gizler; kabul edilebilir biçimi kaydedilen ve düşme oranı
  olarak izlenen yeniden denemedir, karantina ise sahipli ve süreli olmalıdır.

## Sonraki Adım

Kararlı bir test paketi, denetim altındaki bir ortamda beklenen davranışın korunduğunu
söyler. Söylemediği şey, uygulamanın gerçek kullanıcıların elinde nasıl davrandığıdır. Test
makinesi hızlıdır, ağı düzgündür, önbelleği bilinen bir durumdadır; alandaki bir gözlemci
zayıf bir bağlantıda, yaşlı bir aygıtla ve dolu bir ekranla çalışır. Sonraki ders bu iki
veri kaynağını — denetimli koşumdan gelen laboratuvar verisiyle gerçek oturumlardan gelen
alan verisini — yan yana koyar ve aradaki farkı yüzdelik dilim hesabıyla ölçer.
