---
title: 'Tümleşim Testinin Kapsamı'
source: 'https://academia.sh/tr/kurslar/tumlesim-testi/tumlesim-testinin-kapsami'
course: 'Tümleşim, Sözleşme ve Uçtan Uca Test'
language: tr
updated: '2026-08-17T18:10:51+00:00'
license: 'CC BY-SA 4.0'
---

# Tümleşim Testinin Kapsamı

Hangi bağımlılığın gerçeğiyle koşturulacağının bir karar olarak ele alınması: aynı testin sahte ve gerçek katalogla koşturulması, şema uyuşmazlığının yalnız gerçek sınırda yakalanması ve sınırın bedelinin sorgu sayısıyla ölçülmesi.

Birim Testi ve Test Odaklı Geliştirme kursunun kapanışında bir varsayımın adı konmuştu:
oradaki her test süreç içinde ve gerçek bağımlılık olmadan koştu, sahte bağımlılıkların
gerçeğine uyduğu varsayıldı ama hiçbir yerde sınanmadı. Bu kurs o varsayımı sınar. İlk
soru, sınamanın nereden başlayacağıdır.

Yanıt bir erdem değil, bir karardır. "Gerçeğiyle test etmek daha güvenli" cümlesi hiçbir
şey seçmez; her bağımlılık için gerçeğin çalıştırılması ayrı bir bedel getirir ve ayrı bir
kusur sınıfını görünür kılar. Bu ders kararı bir örnek üzerinde verir ve üç şeyi birlikte
ölçer: seçilen sınırın **yakaladığı** kusur sınıfı, **yakalamadığı** kusur sınıfı ve
**bedeli**. Kurs boyunca ölçü ekseni budur.

## Sınır Kararı

Ödünç servisinin dört bağımlılığı vardı: kayıtları tutan katalog, üyeye haber veren
bildirim kanalı, işlem günlüğü ve gün numarasını veren saat. Her biri için iki seçenek
bulunur — sahte bağımlılık ya da gerçeği — ve dördü bağımsız seçildiğinde on altı
yapılandırma çıkar. Bunların hepsi anlamlı değildir. Saatin gerçeği testte hiçbir kusur
sınıfı açığa çıkarmaz, yalnız belirlenimciliği yok eder. Bildirim kanalının gerçeği bir
süreç ayağa kaldırmayı gerektirir. Katalog ise ikisinin arasındadır: gerçeği süreç içinde
çalışabilen bir veri tabanıdır ve kendi kısıtlarını taşır.

Bu ders katalog sınırını gerçek çalıştırır, kalanını sahte bırakır. Bu yapılandırmanın adı
**tümleşim testidir** (integration test): sınanan kod ile en az bir bağımlılığın gerçek
gerçekleştirimi birlikte koşar.

## Aynı Testin İki Yapılandırması

Ödünç servisi, önceki kurstan geldiği biçimiyle duruyor.

```js
// servis.mjs — M21/K02'den gelen odunc servisi: katalog, bildirim ve saat bagimliliklariyla
export const KURALLAR = {
  ogrenci: { oduncGun: 28, enCokOdunc: 10 },
  uye: { oduncGun: 14, enCokOdunc: 5 },
};

export function oduncServisiKur({ katalog, bildirim, saat }) {
  return {
    oduncVer(uye, kitapNo) {
      const kural = KURALLAR[uye.tur];
      if (katalog.uyeOduncSayisi(uye.no) >= kural.enCokOdunc) {
        throw new Error(`odunc siniri asildi: ${kural.enCokOdunc}`);
      }
      const bugun = saat();
      const kayit = { uyeNo: uye.no, kitapNo, alisGunu: bugun, sonGun: bugun + kural.oduncGun };
      katalog.ekle(kayit);
      bildirim.gonder(uye.no, `son tarih: ${kayit.sonGun}`);
      return kayit;
    },
    iadeAl(kitapNo) {
      const kayit = katalog.bul(kitapNo);
      if (kayit === undefined) throw new Error(`odunc kaydi yok: ${kitapNo}`);
      katalog.sil(kitapNo);
      return kayit;
    },
  };
}
```

Katalog arayüzünün iki gerçekleştirimi yazılır. Birincisi önceki kurstaki taklit nesnedir.
İkincisi aynı arayüzü gerçek bir şema üzerinde karşılar; şema, üretimde kullanılan sürümdür
ve kataloğu yazan ekipten gelir.

```js
// kataloglar.mjs — ayni katalog arayuzunun iki gerceklestirimi: taklit nesne ve node:sqlite
import { DatabaseSync } from 'node:sqlite';

export const SEMA = `
CREATE TABLE odunc (
  kitap_no  TEXT PRIMARY KEY,
  uye_no    TEXT NOT NULL,
  sube      TEXT NOT NULL,
  alis_gunu INTEGER NOT NULL,
  son_gun   INTEGER NOT NULL,
  CHECK (son_gun > alis_gunu)
);`;

export function taklitKatalog() {
  const kayitlar = new Map();
  return {
    uyeOduncSayisi: (uyeNo) => [...kayitlar.values()].filter((k) => k.uyeNo === uyeNo).length,
    ekle: (kayit) => kayitlar.set(kayit.kitapNo, kayit),
    bul: (kitapNo) => kayitlar.get(kitapNo),
    sil: (kitapNo) => kayitlar.delete(kitapNo),
    sorguSayisi: () => 0,
  };
}

export function sqliteKatalog(yol = ':memory:') {
  const db = new DatabaseSync(yol);
  db.exec(SEMA);
  let sorgu = 0;
  const calistir = (metin, ...bagli) => { sorgu += 1; return db.prepare(metin).run(...bagli); };
  const oku = (metin, ...bagli) => { sorgu += 1; return db.prepare(metin).get(...bagli); };
  return {
    uyeOduncSayisi: (uyeNo) => oku('SELECT COUNT(*) AS n FROM odunc WHERE uye_no = ?', uyeNo).n,
    ekle: (kayit) => calistir(
      'INSERT INTO odunc (kitap_no, uye_no, sube, alis_gunu, son_gun) VALUES (?, ?, ?, ?, ?)',
      kayit.kitapNo, kayit.uyeNo, kayit.sube ?? null, kayit.alisGunu, kayit.sonGun),
    bul: (kitapNo) => {
      const s = oku('SELECT kitap_no, uye_no, alis_gunu, son_gun FROM odunc WHERE kitap_no = ?', kitapNo);
      return s && { kitapNo: s.kitap_no, uyeNo: s.uye_no, alisGunu: s.alis_gunu, sonGun: s.son_gun };
    },
    sil: (kitapNo) => calistir('DELETE FROM odunc WHERE kitap_no = ?', kitapNo),
    sorguSayisi: () => sorgu,
  };
}
```

Düzenek hangi gerçekleştirimin kurulacağını tek bir yerden seçer. Bildirim bir casus
nesnedir, saat bir işlevdir; ikisi de önceki kurstaki biçimini korur.

```js
// duzenek.mjs — ayni testin iki yapilandirmasi: katalog sahte ya da gercek
import { oduncServisiKur } from './servis.mjs';
import { taklitKatalog, sqliteKatalog } from './kataloglar.mjs';

export function duzenekKur(yapilandirma) {
  const katalog = yapilandirma === 'gercek' ? sqliteKatalog() : taklitKatalog();
  const gonderilen = [];
  const bildirim = { gonder: (uyeNo, metin) => gonderilen.push({ uyeNo, metin }) };
  const servis = oduncServisiKur({ katalog, bildirim, saat: () => 1000 });
  return { servis, katalog, gonderilen };
}

export const UYE = { no: 'U-17', tur: 'uye', sube: 'merkez' };
```

Testler tek dosyadır ve yapılandırmayı bir ortam değişkeninden alır. Üçüncü test, sınır
denetimini sınadığı için kataloğa doğrudan beş kayıt yazar; o kayıtları yazan taraf şemayı
bildiğinden `sube` alanını verir.

```js
// odunc.test.mjs — ayni uc test; KATALOG degiskeni sinirin gercek calisip calismadigini secer
import test from 'node:test';
import assert from 'node:assert/strict';
import { duzenekKur, UYE } from './duzenek.mjs';

const yapilandirma = process.env.KATALOG ?? 'sahte';

test('yeni odunc katalogda gorunur', () => {
  const { servis, katalog } = duzenekKur(yapilandirma);
  servis.oduncVer(UYE, 'K-903');
  assert.equal(katalog.bul('K-903').sonGun, 1014);
});

test('iade edilen kitap katalogdan duser', () => {
  const { servis, katalog } = duzenekKur(yapilandirma);
  servis.oduncVer(UYE, 'K-903');
  servis.iadeAl('K-903');
  assert.equal(katalog.bul('K-903'), undefined);
});

test('odunc siniri asilinca hata verilir', () => {
  const { servis, katalog } = duzenekKur(yapilandirma);
  for (let i = 0; i < 5; i += 1) {
    katalog.ekle({ kitapNo: `K-90${i}`, uyeNo: UYE.no, sube: 'merkez', alisGunu: 1000, sonGun: 1014 });
  }
  assert.throws(() => servis.oduncVer(UYE, 'K-999'), /odunc siniri asildi: 5/);
});
```

## Sahte Sınırda Yeşil

Önceki kursun bıraktığı yapılandırma budur: katalog bir taklit nesnedir.

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

```
ok 1 - yeni odunc katalogda gorunur
ok 2 - iade edilen kitap katalogdan duser
ok 3 - odunc siniri asilinca hata verilir
# tests 3
# pass 3
# fail 0
```

Üç test de geçiyor. Bu koşum, servisin doğru olduğunu değil, servisin **taklit kataloğun
kabul ettiği** her şeyi yaptığını gösterir. Taklit katalog bir eşlemedir ve bir eşleme her
biçimdeki kaydı kabul eder.

## Gerçek Sınırda Kırmızı

Tek değişiklik, katalog sınırının gerçeğiyle koşturulmasıdır. Test dosyası, iddialar ve
servis aynen duruyor.

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

```
not ok 1 - yeni odunc katalogda gorunur
not ok 2 - iade edilen kitap katalogdan duser
ok 3 - odunc siniri asilinca hata verilir
# tests 3
# pass 1
# fail 2
```

İlk iki test düştü. Düşme nedeni koşumun ayrıntısında yazılıdır.

```sh
KATALOG=gercek node --test --test-reporter=tap odunc.test.mjs | grep "error:" | sort -u
```

```
  error: 'NOT NULL constraint failed: odunc.sube'
```

Yakalanan kusur sınıfının adı **şema uyuşmazlığıdır**: servisin ürettiği kayıt, gerçek
şemanın zorunlu tuttuğu bir alanı taşımıyor. Bu kusur koddaydı ve önceki kursun tamamı
boyunca oradaydı; taklit katalog onu göremezdi, çünkü taklit nesnenin kabul kuralı yoktur.
Üçüncü testin yeşil kalması da bilgi taşır: kayıtları şemayı bilerek yazan taraf hatasızdır,
kusur yalnız servisin yazdığı yoldadır.

Düzeltme servisin kayıt biçimindedir.

```js
// servis.mjs — kayit artik sube alanini da tasiyor
export const KURALLAR = {
  ogrenci: { oduncGun: 28, enCokOdunc: 10 },
  uye: { oduncGun: 14, enCokOdunc: 5 },
};

export function oduncServisiKur({ katalog, bildirim, saat }) {
  return {
    oduncVer(uye, kitapNo) {
      const kural = KURALLAR[uye.tur];
      if (katalog.uyeOduncSayisi(uye.no) >= kural.enCokOdunc) {
        throw new Error(`odunc siniri asildi: ${kural.enCokOdunc}`);
      }
      const bugun = saat();
      const kayit = {
        uyeNo: uye.no, kitapNo, sube: uye.sube,
        alisGunu: bugun, sonGun: bugun + kural.oduncGun,
      };
      katalog.ekle(kayit);
      bildirim.gonder(uye.no, `son tarih: ${kayit.sonGun}`);
      return kayit;
    },
    iadeAl(kitapNo) {
      const kayit = katalog.bul(kitapNo);
      if (kayit === undefined) throw new Error(`odunc kaydi yok: ${kitapNo}`);
      katalog.sil(kitapNo);
      return kayit;
    },
  };
}
```

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

```
ok 1 - yeni odunc katalogda gorunur
ok 2 - iade edilen kitap katalogdan duser
ok 3 - odunc siniri asilinca hata verilir
# tests 3
# pass 3
# fail 0
```

## Bu Testin Görmediği

Katalog sınırı gerçek çalışıyor, bildirim sınırı hâlâ bir casus nesne. Bu, bir kusur
sınıfını dokunulmadan bırakır. Bildirimin muhatabına üye numarası yerine üye türünü yazan
bir sürüm ele alınsın.

```js
// kusurlu.mjs — bildirimin muhatabina uye numarasi yerine uye turu yazan surum
import { KURALLAR } from './servis.mjs';

export function kusurluServisKur({ katalog, bildirim, saat }) {
  return {
    oduncVer(uye, kitapNo) {
      const kural = KURALLAR[uye.tur];
      const bugun = saat();
      const kayit = { uyeNo: uye.no, kitapNo, sube: uye.sube, alisGunu: bugun, sonGun: bugun + kural.oduncGun };
      katalog.ekle(kayit);
      bildirim.gonder(uye.tur, `son tarih: ${kayit.sonGun}`);
      return kayit;
    },
  };
}
```

Test, bildirimi de doğruluyor: bir bildirim gitmiş olmalı ve metni doğru son günü
taşımalı.

```js
// kacan.test.mjs — katalog gercek, bildirim casus nesne; muhatap kusuru sinanmis olmuyor
import test from 'node:test';
import assert from 'node:assert/strict';
import { sqliteKatalog } from './kataloglar.mjs';
import { kusurluServisKur } from './kusurlu.mjs';

test('odunc kaydi yazilir ve bir bildirim gonderilir', () => {
  const katalog = sqliteKatalog();
  const gonderilen = [];
  const bildirim = { gonder: (muhatap, metin) => gonderilen.push({ muhatap, metin }) };
  const servis = kusurluServisKur({ katalog, bildirim, saat: () => 1000 });

  servis.oduncVer({ no: 'U-17', tur: 'uye', sube: 'merkez' }, 'K-903');

  assert.equal(katalog.bul('K-903').uyeNo, 'U-17');
  assert.equal(gonderilen.length, 1);
  assert.match(gonderilen[0].metin, /son tarih: 1014/);
  console.log(`gonderilen muhatap: ${gonderilen[0].muhatap}`);
});
```

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

```
# gonderilen muhatap: uye
ok 1 - odunc kaydi yazilir ve bir bildirim gonderilir
# tests 1
# pass 1
# fail 0
```

Test yeşil, muhatap yanlış. Kaçan kusur sınıfının adı **muhatap kimliği uyuşmazlığıdır**:
gerçek bildirim kanalı tanımadığı bir muhatap için hata döndürürdü, casus nesne ise
kendisine verilen her değeri kaydeder. Bu sınıfın yakalanması bildirim sınırının da gerçek
çalıştırılmasını gerektirir; katalog sınırını gerçek yapmak buna hiçbir katkı sağlamaz.
Buradan çıkan kural, kursun tamamına yayılır: bir testin gördüğü kusur sınıfı, gerçek
çalıştırdığı sınırla belirlenir.

## Bedel

Gerçek sınırın karşılığı ölçülebilir. İki nicelik koşumdan bağımsızdır — koşum başına
çalışan sorgu sayısı ve düzeneğin satır sayısı — üçüncüsü, süre, makineye bağlıdır ve ham
milisaniye yerine karşılaştırma olarak bildirilir.

```js
// bedel.mjs — iki yapilandirmanin sorgu sayisi, duzenek satiri ve goreli suresi
import { duzenekKur, UYE } from './duzenek.mjs';
import { taklitKatalog, sqliteKatalog } from './kataloglar.mjs';

const TUR = 500;
const satir = (islev) => islev.toString().split('\n').length - 2;

function olc(yapilandirma) {
  const ilk = duzenekKur(yapilandirma);
  ilk.servis.oduncVer(UYE, 'K-903');
  ilk.servis.iadeAl('K-903');
  const baslangic = performance.now();
  for (let i = 0; i < TUR; i += 1) {
    const { servis } = duzenekKur(yapilandirma);
    servis.oduncVer(UYE, 'K-903');
    servis.iadeAl('K-903');
  }
  return { sure: performance.now() - baslangic, sorgu: ilk.katalog.sorguSayisi() };
}

const sahte = olc('sahte');
const gercek = olc('gercek');

console.log(`sahte katalog : ${TUR} tur, tur basina ${sahte.sorgu} sorgu, katalog ${satir(taklitKatalog)} satir`);
console.log(`gercek katalog: ${TUR} tur, tur basina ${gercek.sorgu} sorgu, katalog ${satir(sqliteKatalog)} satir`);
console.log(`gercek sinir daha uzun surdu      : ${gercek.sure > sahte.sure}`);
console.log(`sure farki en az on kat            : ${gercek.sure / sahte.sure >= 10}`);
```

```
sahte katalog : 500 tur, tur basina 0 sorgu, katalog 8 satir
gercek katalog: 500 tur, tur basina 4 sorgu, katalog 17 satir
gercek sinir daha uzun surdu      : true
sure farki en az on kat            : true
```

Bir ödünç ile bir iade turu, gerçek sınırda dört sorgu çalıştırıyor; sahte sınırda hiç
sorgu yok. Katalog gerçekleştirimi sekiz satırdan on yedi satıra çıkıyor ve bu on yedi
satır artık bakım gerektiren bir düzenek kodudur: şema değişirse burası da değişir. Süre
farkının mutlak değeri makineye ve dosya sistemine bağlıdır, yönü değildir.

Bu üç sayı kararı taşır. Dört sorgu ve on kat süre, bir kusur sınıfının — şema
uyuşmazlığının — karşılığında ödenmiştir. Aynı bedeli saat sınırı için ödemek hiçbir kusur
sınıfı kazandırmaz; bu yüzden saat sahte kalır.

## Özet

- Hangi sınırın gerçek çalıştırılacağı bağımlılık başına verilen bir karardır; dört
  bağımlılık on altı yapılandırma üretir ve hepsi anlamlı değildir.
- Taklit kataloğun kabul kuralı yoktur; gerçek şemanın vardır. Önceki kurs boyunca yeşil
  kalan servis, gerçek şemayla iki testte düştü ve kusurun adı şema uyuşmazlığıdır.
- Bir testin göremediği kusur sınıfı, gerçek çalıştırmadığı sınırla belirlenir: bildirim
  casus nesne kaldığı için muhatap kimliği uyuşmazlığı yeşil kaldı.
- Sınırın bedeli koşumdan bağımsız niceliklerle ölçülür: tur başına dört sorgu ve dokuz
  satırlık ek düzenek kodu.
- Bu kursun her dersi aynı üçlüyü yazar: yakalanan kusur sınıfı, kaçan kusur sınıfı ve
  bedel.

## Sonraki Adım

Katalog sınırı süreç içinde gerçek çalıştı, çünkü kataloğun gerçeği aynı süreçte
yaşayabiliyordu. Bildirim kanalının gerçeği yaşayamaz: ayrı bir süreçtir, bir bağlantı
noktası dinler ve testten önce ayağa kalkmış, testten sonra indirilmiş olması gerekir.
Ayrıca kataloğun kendisi de bellekte değil bir dosyada durursa, şemanın o dosyada kurulmuş
olması bir kurulum adımıdır. Sonraki ders bu adımları test ortamının yönetimi olarak ele
alır: bağımlı servis nasıl ayağa kaldırılır, hazır olduğu nasıl anlaşılır ve bu düzeneğin
her koşumda ödenen bedeli kaç süreç, kaç adım eder.
