---
title: Test
source: 'https://academia.sh/tr/kurslar/nodejs/test'
course: 'Node.js Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:52+00:00'
license: 'CC BY-SA 4.0'
---

# Test

Yerleşik test koşucusu, birim testinin sınırı, sunucuyu dinlemeden ayırarak tümleşim testi yazma, başarısız testin çıktısı ve kapsam raporunun okunması.

Önceki dört ders ölçüm toplayıcının parçalarını kurdu: özetleyici, yapılandırma,
hata sınıfları, kütük ve sunucu. Bu parçaların doğru çalıştığı şimdiye kadar elle,
çıktıya bakılarak denetlendi.

Elle denetimin iki kusuru vardır: her değişiklikten sonra yinelenmesi gerekir ve
kenar durumlar unutulur. Bu ders denetimi yürütülebilir hâle getirir. Test koşucusu
ve doğrulama modülü çalışma zamanının içinde bulunur; belirli bir sürümden itibaren
kararlı sayılırlar.

## Birim Testi

Birim testi, tek bir modülü kendi başına, dış dünyaya dokunmadan sınar. Sınanan
birimin bu yalıtımı destekleyecek biçimde yazılmış olması gerekir — HTTP Sunucusu
dersinde özetleme mantığının ayrı modüle alınmasının nedeni buydu.

```js
// ozetleyici.mjs
export function satirdanKayit(satir) {
  const kayit = JSON.parse(satir);
  if (typeof kayit.dugum !== 'string' || typeof kayit.metrik !== 'string') {
    throw new TypeError('dugum ve metrik dizgi olmali');
  }
  if (!Number.isFinite(kayit.deger)) {
    throw new TypeError('deger sonlu bir sayi olmali');
  }
  return kayit;
}

export class Ozetleyici {
  #kovalar = new Map();

  ekle({ dugum, metrik, deger }) {
    const anahtar = `${dugum}/${metrik}`;
    const kova = this.#kovalar.get(anahtar) ?? { sayi: 0, toplam: 0, enBuyuk: -Infinity };
    kova.sayi += 1;
    kova.toplam += deger;
    kova.enBuyuk = Math.max(kova.enBuyuk, deger);
    this.#kovalar.set(anahtar, kova);
  }

  ozet() {
    return [...this.#kovalar].map(([anahtar, { sayi, toplam, enBuyuk }]) => ({
      anahtar,
      sayi,
      ortalama: Number((toplam / sayi).toFixed(2)),
      enBuyuk,
    }));
  }
}
```

```js
// ozetleyici.test.mjs
import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
import { Ozetleyici, satirdanKayit } from './ozetleyici.mjs';

describe('satirdanKayit', () => {
  test('gecerli satiri kayda cevirir', () => {
    const kayit = satirdanKayit('{"dugum":"kenar-01","metrik":"nem","deger":48}');
    assert.deepEqual(kayit, { dugum: 'kenar-01', metrik: 'nem', deger: 48 });
  });

  test('bozuk JSON icin SyntaxError firlatir', () => {
    assert.throws(() => satirdanKayit('bozuk'), SyntaxError);
  });

  test('sayisal olmayan deger icin TypeError firlatir', () => {
    assert.throws(
      () => satirdanKayit('{"dugum":"a","metrik":"b","deger":"yirmi"}'),
      { name: 'TypeError' },
    );
  });
});

describe('Ozetleyici', () => {
  test('ayni anahtardaki olcumleri birlestirir', () => {
    const o = new Ozetleyici();
    o.ekle({ dugum: 'kenar-01', metrik: 'sicaklik', deger: 21.4 });
    o.ekle({ dugum: 'kenar-01', metrik: 'sicaklik', deger: 22.6 });
    assert.deepEqual(o.ozet(), [
      { anahtar: 'kenar-01/sicaklik', sayi: 2, ortalama: 22, enBuyuk: 22.6 },
    ]);
  });

  test('bos ozetleyici bos dizi dondurur', () => {
    assert.deepEqual(new Ozetleyici().ozet(), []);
  });
});
```

```sh
node --test ozetleyici.test.mjs
```

```
▶ satirdanKayit
  ✔ gecerli satiri kayda cevirir (1.11775ms)
  ✔ bozuk JSON icin SyntaxError firlatir (0.155333ms)
  ✔ sayisal olmayan deger icin TypeError firlatir (0.074167ms)
✔ satirdanKayit (2.002583ms)
▶ Ozetleyici
  ✔ ayni anahtardaki olcumleri birlestirir (0.125584ms)
  ✔ bos ozetleyici bos dizi dondurur (0.046625ms)
✔ Ozetleyici (0.240542ms)
ℹ tests 5
ℹ suites 2
ℹ pass 5
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 34.86225
```

Süre değerleri her çalıştırmada değişir; anlamlı olan sayaçlardır.

`node:assert/strict` alt yolu, karşılaştırmaları katı eşitlikle yapan biçimi verir.
JavaScript Temelleri kursunda gevşek eşitliğin tuzakları anlatılmıştı; testte gevşek
karşılaştırma, geçmemesi gereken bir testin geçmesine yol açar.

`assert.throws` çağrısının ikinci argümanı beklenen hatayı tanımlar. Bir yapıcı
verilebilir, bir nesne verilerek alanlar sınanabilir. Yalnızca "bir hata fırlatsın"
demek zayıf bir sınamadır: yanlış nedenle fırlayan hata da testi geçirir.

## Bağımlılığı Dışarıdan Vermek

Günlükleme dersindeki kütük oluşturucu, saat fonksiyonunu ve çıktı akışını dışarıdan
alıyordu. Bu tasarım tam olarak sınanabilirlik içindir:

```js
// kutuk.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Writable } from 'node:stream';
import { kutukOlustur } from './kutuk.mjs';

function yakalayanCikti() {
  const satirlar = [];
  const akis = new Writable({
    write(parca, kodlama, bitti) { satirlar.push(parca.toString('utf8')); bitti(); },
  });
  return { akis, satirlar };
}

test('esigin altindaki kayit yazilmaz', () => {
  const { akis, satirlar } = yakalayanCikti();
  const kutuk = kutukOlustur({ duzey: 'uyari', hizmet: 'sinama', cikti: akis, saat: () => 'T' });

  kutuk.bilgi('gorunmemeli');
  kutuk.uyari('gorunmeli');

  assert.equal(satirlar.length, 1);
  assert.deepEqual(JSON.parse(satirlar[0]), {
    zaman: 'T', duzey: 'uyari', hizmet: 'sinama', ileti: 'gorunmeli',
  });
});

test('alt kutuk sabit alanlari her satira ekler', () => {
  const { akis, satirlar } = yakalayanCikti();
  const kutuk = kutukOlustur({ hizmet: 'sinama', cikti: akis, saat: () => 'T' });

  kutuk.alt({ istekKimligi: 'r-1' }).bilgi('ilk', { durum: 200 });

  assert.equal(JSON.parse(satirlar[0]).istekKimligi, 'r-1');
});
```

Sabit saat ve toplayan akış sayesinde test, gerçek zamana ve gerçek çıktıya
dokunmadan tam kaydı doğrular. Modülün içindeki bir değeri sınama sırasında
değiştirmeye gerek kalmaz — bağımlılık zaten dışarıdan verilmektedir.

## Tümleşim Testi

Tümleşim testi birkaç parçayı birlikte sınar. HTTP hizmeti için sorun şudur:
`hizmet.mjs` içe aktarıldığı anda dinlemeye başlar, bu yüzden test içinden
denetlenemez.

Çözüm, sunucuyu **kurmakla dinlemeye başlamayı ayırmaktır**: modül bir fabrika
fonksiyonu dışa aktarır, dinleme kararını çağıran verir.

```js
// sunucu-olustur.mjs
import { createServer } from 'node:http';
import { Ozetleyici, satirdanKayit } from './ozetleyici.mjs';

export function sunucuOlustur() {
  const ozetleyici = new Ozetleyici();

  return createServer(async (istek, yanit) => {
    const adres = new URL(istek.url, `http://${istek.headers.host}`);

    if (istek.method === 'POST' && adres.pathname === '/olcum') {
      const parcalar = [];
      for await (const parca of istek) parcalar.push(parca);
      const govde = Buffer.concat(parcalar).toString('utf8');
      try {
        let sayac = 0;
        for (const satir of govde.split('\n')) {
          if (satir.trim() === '') continue;
          ozetleyici.ekle(satirdanKayit(satir));
          sayac += 1;
        }
        yanit.writeHead(201, { 'content-type': 'application/json' });
        yanit.end(JSON.stringify({ alinan: sayac }));
      } catch (hata) {
        yanit.writeHead(400, { 'content-type': 'application/json' });
        yanit.end(JSON.stringify({ hata: hata.message }));
      }
      return;
    }

    if (istek.method === 'GET' && adres.pathname === '/ozet') {
      yanit.writeHead(200, { 'content-type': 'application/json' });
      yanit.end(JSON.stringify(ozetleyici.ozet()));
      return;
    }

    yanit.writeHead(404, { 'content-type': 'application/json' });
    yanit.end(JSON.stringify({ hata: 'bulunamadi' }));
  });
}
```

```js
// sunucu.test.mjs
import { test, before, after, describe } from 'node:test';
import assert from 'node:assert/strict';
import { once } from 'node:events';
import { sunucuOlustur } from './sunucu-olustur.mjs';

describe('olcum hizmeti', () => {
  let sunucu;
  let taban;

  before(async () => {
    sunucu = sunucuOlustur();
    sunucu.listen(0, '127.0.0.1');            // 0: bos bir baglanti noktasini cekirdek secer
    await once(sunucu, 'listening');
    taban = `http://127.0.0.1:${sunucu.address().port}`;
  });

  after(async () => {
    sunucu.close();
    await once(sunucu, 'close');
  });

  test('olcum gonderimi 201 doner', async () => {
    const yanit = await fetch(`${taban}/olcum`, {
      method: 'POST',
      body: '{"dugum":"kenar-01","metrik":"nem","deger":48}\n',
    });
    assert.equal(yanit.status, 201);
    assert.deepEqual(await yanit.json(), { alinan: 1 });
  });

  test('ozet gonderilen olcumu yansitir', async () => {
    const yanit = await fetch(`${taban}/ozet`);
    assert.equal(yanit.status, 200);
    assert.deepEqual(await yanit.json(), [
      { anahtar: 'kenar-01/nem', sayi: 1, ortalama: 48, enBuyuk: 48 },
    ]);
  });

  test('bilinmeyen yol 404 doner', async () => {
    const yanit = await fetch(`${taban}/yok`);
    assert.equal(yanit.status, 404);
  });
});
```

```sh
node --test sunucu.test.mjs
```

```
▶ olcum hizmeti
  ✔ olcum gonderimi 201 doner (10.196542ms)
  ✔ ozet gonderilen olcumu yansitir (1.354708ms)
  ✔ bilinmeyen yol 404 doner (0.68975ms)
✔ olcum hizmeti (16.061875ms)
ℹ tests 3
ℹ suites 1
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 73.281666
```

İki ayrıntı bu testi güvenilir kılar.

Bağlantı noktası olarak sıfır verildi: çekirdek boş bir geçici bağlantı noktası
seçer ve gerçek numara `address()` çağrısıyla öğrenilir. Sabit bir numara yazmak,
aynı anda çalışan iki testin çakışmasına yol açar.

`after` kancası sunucuyu kapatıyor ve kapanmayı bekliyor. Kapatılmayan bir sunucu
olay döngüsünü ayakta tutar ve test süreci bitmez.

Test sırasında istek `fetch` ile atıldı — barındıran ortamın web platformundan
devraldığı arayüzlerden biri. İstemci için ayrı bir bağımlılık gerekmedi.

Uçtan uca test bir adım daha ileri gider: yapıyı gerçek bir süreç olarak başlatır,
gerçek yapılandırmayla çalıştırır ve dışarıdan istek atar. Alt Süreçler dersindeki
`spawn` bu iş için kullanılır. Uçtan uca testler yavaş ve kırılgandır; sayıları az
tutulur, kritik yollarla sınırlanır.

## Başarısız Test ve Kapsam

Bir testin başarısız olduğunda ne yazdığını görmek, çıktıyı okumayı öğrenmenin en
kısa yoludur. Aşağıdaki test bilerek eksik bir beklenti taşıyor:

```js
// basarisiz.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Ozetleyici } from './ozetleyici.mjs';

test('ozet alanlari', () => {
  const o = new Ozetleyici();
  o.ekle({ dugum: 'kenar-01', metrik: 'nem', deger: 48 });
  assert.deepEqual(o.ozet(), [{ anahtar: 'kenar-01/nem', sayi: 1, ortalama: 48 }]);
});
```

```sh
node --test basarisiz.test.mjs; echo "cikis kodu: $?"
```

```
✖ ozet alanlari (0.978292ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 33.174375

✖ failing tests:

test at basarisiz.test.mjs:5:1
✖ ozet alanlari (0.978292ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
  + actual - expected
  
    [
      {
        anahtar: 'kenar-01/nem',
  +     enBuyuk: 48,
        ortalama: 48,
cikis kodu: 1
```

Fark raporu, gerçek değerde bulunup beklenende olmayan alanı `+` ile işaretliyor.
Sıfırdan farklı çıkış kodu, testin bir otomasyon adımından çağrıldığında
başarısızlığı bildirmesini sağlar.

Hangi satırların hiç çalışmadığını görmek için kapsam ölçümü açılır:

```sh
node --test --experimental-test-coverage ozetleyici.test.mjs
```

```
ℹ start of coverage report
ℹ ---------------------------------------------------------------
ℹ file           | line % | branch % | funcs % | uncovered lines
ℹ ---------------------------------------------------------------
ℹ ozetleyici.mjs |  93.75 |    90.91 |  100.00 | 4-5
ℹ ---------------------------------------------------------------
ℹ all files      |  93.75 |    90.91 |  100.00 | 
ℹ ---------------------------------------------------------------
ℹ end of coverage report
```

Rapor, 4 ve 5 numaralı satırların hiç çalışmadığını söylüyor: bunlar `dugum` alanı
dizgi olmadığında çalışan dal. Kapsam yüzdesi bir hedef değil bir işarettir — hangi
davranışın hiç sınanmadığını gösterir, sınanan davranışların doğru sınandığını
göstermez.

## Özet

- Birim testi tek modülü yalıtımda sınar; bu yalıtım, mantığın arayüzden ayrı bir
  modülde tutulmasını gerektirir.
- Bağımlılıklar (saat, çıktı akışı) dışarıdan verildiğinde test gerçek zamana ve
  gerçek çıktıya dokunmadan tam kaydı doğrular.
- Sunucu kurmak ile dinlemeye başlamak ayrıldığında hizmet tümleşim testine açılır;
  test sıfır numaralı bağlantı noktası ister ve sonunda sunucuyu kapatır.
- Katı doğrulama biçimi gevşek eşitliğin tuzaklarını dışarıda bırakır; fırlatma
  sınamalarında beklenen hata tipi de belirtilir.
- Kapsam raporu hangi davranışın hiç sınanmadığını gösterir; sınananların doğru
  sınandığını göstermez.

## Sonraki Adım

Testler, beklenen davranışın korunduğunu söyler; beklenmeyen bir davranışın nedenini
söylemez. Süreç yavaşladığında, belleği büyüdüğünde veya beklenmedik bir yerde
takıldığında başka araçlar gerekir. Sonraki ders çalışma zamanının inceleme
arayüzünü, işlemci profilini ve öbek anlık görüntüsünü ele alır.
