---
title: 'Eski Kodu Test Altına Alma'
source: 'https://academia.sh/tr/kurslar/birim-testi/eski-kodu-test-altina-alma'
course: 'Birim Testi ve Test Odaklı Geliştirme'
language: tr
updated: '2026-08-17T18:10:54+00:00'
license: 'CC BY-SA 4.0'
---

# Eski Kodu Test Altına Alma

Dikişsiz bir işlevin mevcut davranışının karakterizasyon testleriyle kayda geçirilmesi, parametre dikişi açılarak değiştirilebilir hâle getirilmesi ve aynı testlerin dikişten sonra da yeşil kalması.

Bu konuda kurulan her şey boş bir sayfadan başladı: test önce yazıldı, kod sonra geldi.
Var olan bir kod tabanında bu sıra kurulamaz. Yıllardır çalışan bir işlev zaten oradadır,
davranışı yazılı değildir ve çoğu zaman kimse tam olarak ne yaptığını bilmez.

Böyle bir işlevi değiştirmenin sırası tersinedir: önce **ne yaptığı** kayda geçirilir, sonra
**değiştirilebilir** hâle getirilir. Birinci adımın aracı karakterizasyon testleridir,
ikincisininki dikiştir.

## Dikişsiz Kod

Aşağıdaki işlev gecikme raporu üretir. Kayıtları modül düzeyinde tutar, günü sistem
saatinden okur ve sonucu doğrudan çıktı akışına yazar.

```js
// eski-rapor.mjs — yillardir calisan, testi olmayan surum
const KAYITLAR = [
  { kitapNo: 'K-903', uyeNo: 'U-17', sonGun: 1010, sube: 'merkez', iadeSubesi: 'merkez', iadeGunu: 1020 },
  { kitapNo: 'K-101', uyeNo: 'U-42', sonGun: 1024, sube: 'merkez', iadeSubesi: 'yamac', iadeGunu: 1024 },
  { kitapNo: 'K-555', uyeNo: 'U-58', sonGun: 1000, sube: 'yamac', iadeSubesi: 'yamac', iadeGunu: 1100 },
];

export function gecikmeRaporuYaz() {
  const bugun = Math.floor(Date.now() / 86400000);
  let toplam = 0;
  console.log(`gecikme raporu — gun ${bugun}`);
  for (const kayit of KAYITLAR) {
    const gecikme = kayit.iadeGunu - kayit.sonGun;
    let ucret = 0;
    if (gecikme > 3) ucret = (gecikme - 3) * 2;
    if (kayit.iadeSubesi !== kayit.sube) ucret += 5;
    if (ucret > 40) ucret = 40;
    toplam += ucret;
    console.log(`${kayit.kitapNo} ${kayit.uyeNo} gecikme ${gecikme} ucret ${ucret}`);
  }
  console.log(`toplam ${toplam}`);
}
```

Bir kod parçasında **dikiş** (seam), davranışı kaynağı değiştirilmeden
başkalaştırılabilen noktadır. Bu işlevde öyle bir nokta yoktur. Girdisi yoktur — kayıtlar içeridedir. Zamanı
dışarıdan alamaz — saati kendisi okur. Sonucu döndürmez — yazar. Beşinci dersteki ölçüm
tam olarak bu üç bağımlılığı adlandırmıştı; burada üçü aynı işlevde birleşmiştir.

Yine de bir gözlem noktası vardır: yazdığı satırlar. Küresel nesneler değiştirilerek hem
saat sabitlenebilir hem yazılanlar toplanabilir. Beşinci derste bu yolun pahalı olduğu
söylenmişti — burada geçici olarak tek yol odur.

```js
// gozlem.mjs — eski islevin bugunku ciktisi kayda geciriliyor
import { gecikmeRaporuYaz } from './eski-rapor.mjs';

const gercekNow = Date.now;
Date.now = () => 1020 * 86400000;
try {
  gecikmeRaporuYaz();
} finally {
  Date.now = gercekNow;
}
```

```
gecikme raporu — gun 1020
K-903 U-17 gecikme 10 ucret 14
K-101 U-42 gecikme 0 ucret 5
K-555 U-58 gecikme 100 ucret 40
toplam 59
```

İkinci satırda dikkat çeken bir şey var: K-101 kaydının gecikmesi sıfır, ücreti beş. Yedinci
derste bunun bir hata olduğu belirlenmişti — zamanında iade edilen kitaba şube dışı ücreti
uygulanıyor. Karakterizasyon testinin buradaki tavrı belirleyicidir.

## Karakterizasyon Testleri

**Karakterizasyon testi** (characterization test), kodun ne yapması gerektiğini değil, şu
anda **ne yaptığını** kayda geçiren testtir. Beklenen değeri belirtimden almaz; koşumdan
alır. Amacı doğrulamak değil, ileride yapılacak değişikliğin fark edilmesini sağlamaktır.

Bu, birinci derste konulan kuralın kasıtlı bir istisnasıdır. Orada beklenen değerin sınanan
koddan bağımsız bir kaynaktan gelmesi gerektiği söylenmişti; burada beklenen değer doğrudan
sınanan koddan gelir. Fark amaçtadır: bu testler doğruluk iddiası taşımaz, bir **taban
çizgisi** kurar.

```js
// karakterizasyon.test.mjs — mevcut davranisi oldugu gibi kayda geciren testler
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { gecikmeRaporuYaz } from './eski-rapor.mjs';

function yakala(bugun) {
  const gercekNow = Date.now;
  const gercekLog = console.log;
  const satirlar = [];
  Date.now = () => bugun * 86400000;
  console.log = (satir) => satirlar.push(satir);
  try {
    gecikmeRaporuYaz();
  } finally {
    Date.now = gercekNow;
    console.log = gercekLog;
  }
  return satirlar;
}

test('rapor basligi bugunun gun numarasini tasir', () => {
  assert.equal(yakala(1020)[0], 'gecikme raporu — gun 1020');
});

test('rapor satirlari bugunku haliyle uretilir', () => {
  assert.deepEqual(yakala(1020).slice(1), [
    'K-903 U-17 gecikme 10 ucret 14',
    'K-101 U-42 gecikme 0 ucret 5',
    'K-555 U-58 gecikme 100 ucret 40',
    'toplam 59',
  ]);
});

test('rapor icerigi bugunun gun numarasindan etkilenmez', () => {
  assert.deepEqual(yakala(2000).slice(1), yakala(1020).slice(1));
});
```

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

```
ok 1 - rapor basligi bugunun gun numarasini tasir
ok 2 - rapor satirlari bugunku haliyle uretilir
ok 3 - rapor icerigi bugunun gun numarasindan etkilenmez
# tests 3
# pass 3
# fail 0
```

İkinci test, bilinen hatayı da kayda geçiriyor: `K-101 U-42 gecikme 0 ucret 5` satırı
beklenen sonucun içinde. Bu bilerek yapılır. Karakterizasyon aşamasında hata düzeltilirse
iki değişiklik aynı anda yapılmış olur — yapı değişikliği ile davranış değişikliği
birbirine karışır ve düşen bir test hangisinden geldiğini söyleyemez.

Üçüncü test bir belirsizliği kapatıyor: rapor içeriğinin bugüne bağlı olmadığı, iki farklı
gün numarasıyla alınan sonuçların karşılaştırılmasıyla kayda geçiyor. Karakterizasyon
testleri yazılırken bu tür sorular sistematik olarak sorulur, çünkü bilinmeyen bağımlılık
sonraki adımı bozan şeydir.

## Dikiş Açmak

Taban çizgisi kurulduktan sonra yapı değiştirilebilir. En ucuz dikiş türü **parametre
dikişidir**: içeriden okunan değer bir parametreye taşınır ve eski çağıranlar için o
parametreyi dolduran ince bir sarmalayıcı bırakılır.

```js
// eski-rapor.mjs — surum 2: parametre dikisi acildi, davranis aynen korundu
const KAYITLAR = [
  { kitapNo: 'K-903', uyeNo: 'U-17', sonGun: 1010, sube: 'merkez', iadeSubesi: 'merkez', iadeGunu: 1020 },
  { kitapNo: 'K-101', uyeNo: 'U-42', sonGun: 1024, sube: 'merkez', iadeSubesi: 'yamac', iadeGunu: 1024 },
  { kitapNo: 'K-555', uyeNo: 'U-58', sonGun: 1000, sube: 'yamac', iadeSubesi: 'yamac', iadeGunu: 1100 },
];

export function raporSatirlari(kayitlar, bugun) {
  const satirlar = [`gecikme raporu — gun ${bugun}`];
  let toplam = 0;
  for (const kayit of kayitlar) {
    const gecikme = kayit.iadeGunu - kayit.sonGun;
    let ucret = 0;
    if (gecikme > 3) ucret = (gecikme - 3) * 2;
    if (kayit.iadeSubesi !== kayit.sube) ucret += 5;
    if (ucret > 40) ucret = 40;
    toplam += ucret;
    satirlar.push(`${kayit.kitapNo} ${kayit.uyeNo} gecikme ${gecikme} ucret ${ucret}`);
  }
  satirlar.push(`toplam ${toplam}`);
  return satirlar;
}

export function gecikmeRaporuYaz() {
  const bugun = Math.floor(Date.now() / 86400000);
  for (const satir of raporSatirlari(KAYITLAR, bugun)) console.log(satir);
}
```

Üç şey aynı anda yapıldı: kayıtlar ve gün parametreye taşındı, çıktı yazma işi hesaptan
ayrıldı ve eski dış arayüz aynen korundu. Bu bir yeniden düzenlemedir — davranış
değişmemelidir. Doğrulaması, **hiç değiştirilmemiş** karakterizasyon testlerinin yeniden
koşturulmasıdır.

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

```
ok 1 - rapor basligi bugunun gun numarasini tasir
ok 2 - rapor satirlari bugunku haliyle uretilir
ok 3 - rapor icerigi bugunun gun numarasindan etkilenmez
# tests 3
# pass 3
# fail 0
```

Parametre dikişi tek seçenek değildir. **Nesne dikişi**, bağımlılığı bir nesne alanı olarak
verir — beşinci dersteki servis kurulumu bunun örneğidir. **Modül dikişi**, çağrılan modülün
kendisini değiştirilebilir kılar. Üçünün ortak yanı, davranışın kaynak değiştirmeden
başkalaştırılabildiği bir nokta yaratmalarıdır; farkı maliyet ve okunabilirliktedir.

## Dikişten Geçen Testler

Dikiş açıldıktan sonra yazılan testler küresel nesnelere dokunmaz. Saat parametredir,
kayıtlar parametredir, sonuç dönüş değeridir.

```js
// dikis.test.mjs — dikisten gecen testler: saat ve kayitlar disaridan veriliyor
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { raporSatirlari } from './eski-rapor.mjs';

const kayit = (ek) => ({
  kitapNo: 'K-903', uyeNo: 'U-17', sonGun: 1010, sube: 'merkez', iadeSubesi: 'merkez', ...ek,
});

test('ucretsiz gun payi icindeki gecikme ucretlendirilmez', () => {
  const satirlar = raporSatirlari([kayit({ iadeGunu: 1013 })], 1020);

  assert.equal(satirlar[1], 'K-903 U-17 gecikme 3 ucret 0');
});

test('gecikmesiz sube disi iade bes birim ucretlendirilir', () => {
  const satirlar = raporSatirlari([kayit({ iadeGunu: 1010, iadeSubesi: 'yamac' })], 1020);

  assert.equal(satirlar[1], 'K-903 U-17 gecikme 0 ucret 5');
});

test('bos kayit listesi toplami sifir verir', () => {
  assert.deepEqual(raporSatirlari([], 1020), ['gecikme raporu — gun 1020', 'toplam 0']);
});
```

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

```
ok 1 - ucretsiz gun payi icindeki gecikme ucretlendirilmez
ok 2 - gecikmesiz sube disi iade bes birim ucretlendirilir
ok 3 - bos kayit listesi toplami sifir verir
# tests 3
# pass 3
# fail 0
```

İkinci testin adı bilinen hatayı **açıkça** adlandırıyor. Bu, karakterizasyon aşamasından
kalan mirasın son hâlidir: davranış artık gizli bir yan etki değil, adı konmuş ve sınanan
bir kural. Düzeltme kararı verildiğinde yapılacak iş bellidir — testin adı ve beklentisi
değişir, sonra gerçekleştirim; ve bu artık üçüncü dersteki sıradan kırmızı–yeşil turudur.

Sıra da bu yüzden önemlidir. Karakterizasyon testleri olmadan yapılan bir dikiş açma
işleminde, davranışın korunup korunmadığını söyleyen hiçbir şey yoktur. Testler önce yazılır,
yapı sonra değişir, düzeltme en sonda gelir.

## Özet

- Dikiş, bir kodun davranışının kaynağı değiştirilmeden başkalaştırılabildiği noktadır;
  dikişsiz kodda tek gözlem noktası küresel nesneler olabilir.
- Karakterizasyon testi kodun ne yapması gerektiğini değil şu anda ne yaptığını kayda geçirir;
  beklenen değeri belirtimden değil koşumdan alır.
- Bilinen hatalar da taban çizgisine dahil edilir; düzeltme ile yapı değişikliği aynı anda
  yapılmaz.
- Parametre dikişi içeriden okunan değeri parametreye taşır ve eski arayüzü ince bir
  sarmalayıcıyla korur; nesne dikişi ve modül dikişi aynı amacın başka maliyetli biçimleridir.
- Dikişten geçen testler küresel nesnelere dokunmaz; sıra her zaman karakterizasyon, dikiş,
  düzeltme biçimindedir.

## Kurs Kapanışı

Bu kurs iki konuda tek bir soruyu iki yönden ele aldı: bir birim testi neyi kanıtlar ve o
kanıt nasıl güvenilir tutulur?

Birim Test Pratiği konusu testi bir ölçüm aracına çevirdi. Testin gövdesi hazırla, uygula ve
doğrula bölümlerine ayrıldı; iddianın sinyal gücü yakaladığı bozulma sayısıyla, adlandırmanın
değeri düşen koşumun taşıdığı bilgiyle, bağımsızlık sıra permütasyonuyla, düzenek maliyeti
satır sayısıyla, kapsam üç ayrı birimin elle sayımıyla ve kırılganlık yeniden düzenleme
karşısında düşen test sayısıyla ölçüldü. Sahte bağımlılıkların beş türü elle yazıldı ve her
birinin yanıtladığı soru ayrıldı.

Test Odaklı Geliştirme konusu sırayı tersine çevirdi. Kırmızı–yeşil–yeniden düzenle döngüsü
adım adım koşturuldu, adım boyunun geri bildirim süresine etkisi arama alanıyla ölçüldü,
kurallar alan diliyle yazılmış örnek tablolarına taşındı, kabul ölçütleri dış halka olarak
kuruldu ve son olarak testi olmayan bir kod karakterizasyon testleriyle taban çizgisine
oturtulup dikişle açıldı.

Kursun boyunca sessizce taşınan bir varsayım var ve adının konması gerekiyor. Buradaki her
test **süreç içinde** ve **gerçek bağımlılık olmadan** koştu. Katalog bir eşlemeydi, depo
bir taklit nesneydi, bildirim bir casus nesneydi, saat bir işlevdi. Bu sahte bağımlılıkların
gerçeğine uyduğu varsayıldı — ama hiçbir yerde sınanmadı. Taklit katalog gerçek veri
tabanının kısıtlarını taşımıyor; casus bildirim, gerçek bildirim kanalının başarısızlıklarını
üretmiyor; sürücünün kurduğu dünya, gerçek bir dağıtımda karşılaşılacak sınırların hiçbirini
içermiyor.

Bu varsayımın sınanması ayrı bir test katmanının konusudur. Tümleşim, Sözleşme ve Uçtan Uca
Test kursu bu sınırları gerçek çalıştırarak ele alır: hangi bağımlılığın gerçeği kullanılır,
sahte bağımlılık ile gerçeğin aynı sözleşmeye uyduğu nasıl güvenceye alınır ve uygulamanın
tamamı üzerinden koşan testler nasıl kararlı tutulur.
