---
title: Test
source: 'https://academia.sh/tr/kurslar/web-uygulama-cercevesi/test'
course: 'Web Uygulama Çerçevesi'
language: tr
updated: '2026-10-21T05:03:39+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'İstekten yanıta giden bir özellik testini küçük bir doğrulama fonksiyonuyla yazar'
    - 'Sahte kuyrukta işin kaydedildiğini ama koşmadığını ölçer'
    - 'Özellik testinin geçtiği yerde işin gövdesini birim testiyle ayrıca sınar'
---

# Test

Özellik testi ve birim testi, küçük bir doğrulama fonksiyonu; sahte kuyrukta işin kaydedilip koşmaması, kuyruk boşaltılınca işin koşması ve sahte kuyruğun işin gövdesini ölçmemesi.

Yerelleştirme dersinde yedek dil, eksik bir çeviriyi hatasız bir yanıtın arkasına sakladı. Önceki derslerde de aynı şey yaşandı: kuyruğa konan iş yanıttan sonra koşar, yanıt ise işi beklemeden döner. Peki testin içinde, gerçek bir işçi yokken, işin koştuğunu nasıl bilebiliriz? Bu ders şunu sorar: **bir test işin kaydedildiğini doğruluyorsa, işin koştuğunu da doğrulamış olur mu?**

**Özellik testi (feature test)** bir isteği uygulamaya verir ve yanıtı, yanında bırakılan izleri doğrular. **Birim testi (unit test)** tek bir sınıfı ya da fonksiyonu, çevresinden ayırarak sınar. **Sahte nesne (fake)** gerçek bir bağımlılığın yerine konan ve yaptıklarını kaydeden basit bir benzeridir: bu derste sahte kuyruk işi çalıştırmaz, yalnız saklar.

**US12.** Test kitaplığı kullanılmaz. Ders, `Test` sınıfında iki fonksiyonlu bir doğrulayıcı kurar: `dogrula()` koşul yanlışsa bir hata fırlatır, `calistir()` bir testi çalıştırıp geçti ya da kaldı olarak yazar. Eşitlik, dizi karşılaştırması, ayrıntılı fark çıktısı ve test keşfi yoktur; her test elle çağrılır.

**US13.** Kuyruk gerçek değildir: `SahteKuyruk` işi bir diziye koyar ve `isle()` çağrılana kadar koşturmaz. `koy()` işi `ertele()` ile işaretler, `isle()` ise her işi `kostu()` ile işaretleyip çalıştırır. Gerçek bir sürücü, yeniden deneme, gecikme ve hata yönetimi modellenmez. İş küçük bir hesaptır: genişliği dörde bölen `KucukResimIsi` ve bilerek yanlış çalışan `HataliKucukResimIsi`.

## Ölçüm Çekirdeği

Kurs boyunca her derste aynı ölçüm kullanılır. `kostu.php` beklenen bir parçanın bu istekte koşup koşmadığını kaydeder; dosya her derste birebir verilir ve çıktı bloğu yoktur, yalnız diske yazılır. `Gunluk` üç liste tutar: isteğin yoluna konanlar, istek içinde çağrılanlar ve isteğin dışına bırakılanlar. `sinifla()` bunlara sırayla bakar. **Karar sırası `sinifla()`'nın kendisidir:** koştu, ertelendi, atlandı, bağlanmadı. Parçayı geliştirici adlandırır; ölçülen, geliştiricinin bu istekte koşmasını beklediği parçalardır. Bu derste ölçülen parça `kucukresim` işidir: iş kaydedilince `yola()` ve `ertele()`, kuyruk boşaltılınca `kostu()` çağrılır.

```php
<?php
// kostu.php — beklenen bir parça bu istekte koştu mu?
declare(strict_types=1);

final class Gunluk {
    public static array $yol = [];         // isteğin yoluna konan parçalar
    public static array $kostu = [];       // istek içinde çağrılanlar
    public static array $ertelenen = [];   // isteğin dışına bırakılanlar
    public static function yeniIstek(): void { self::$yol = self::$kostu = self::$ertelenen = []; }
    public static function yola(string $parca): void { self::$yol[] = $parca; }
    public static function kostu(string $parca): void { self::$kostu[] = $parca; }
    public static function ertele(string $parca): void { self::$ertelenen[] = $parca; }
}

function sinifla(string $parca): string {
    return match (true) {
        in_array($parca, Gunluk::$kostu, true) => 'koştu',
        in_array($parca, Gunluk::$ertelenen, true) => 'ertelendi',
        in_array($parca, Gunluk::$yol, true) => 'atlandı',
        default => 'bağlanmadı',
    };
}
```

## Sınanan Kod

`uygulama.php` sınanan her şeyi tutar: `Is` arayüzü, iki iş, `SahteKuyruk` ve `YuklemeDenetleyicisi`. Denetleyici bir genişlik alır, işi kuyruğa koyar ve 201 ile döner. Hangi iş sınıfının kullanılacağı kurucuya verilir; varsayılan doğru olandır. `test.php` ise doğrulayıcıdır.

```php
<?php
// uygulama.php — sınanan kod: iş arayüzü, sahte kuyruk ve yükleme denetleyicisi
declare(strict_types=1);
require_once 'kostu.php';

interface Is {
    public function ad(): string;
    public function calistir(): int;
}

final class KucukResimIsi implements Is {
    public function __construct(private int $genislik) {}
    public function ad(): string { return 'kucukresim'; }
    public function calistir(): int { return intdiv($this->genislik, 4); }
}

final class HataliKucukResimIsi implements Is {
    public function __construct(private int $genislik) {}
    public function ad(): string { return 'kucukresim'; }
    public function calistir(): int { return $this->genislik * 4; }   // yanlış yön
}

final class SahteKuyruk {
    public array $isler = [];
    public function koy(Is $is): void {
        $this->isler[] = $is;
        Gunluk::ertele($is->ad());
    }
    public function isle(): array {
        $sonuclar = [];
        foreach ($this->isler as $is) {
            Gunluk::kostu($is->ad());
            $sonuclar[] = $is->calistir();
        }
        $this->isler = [];
        return $sonuclar;
    }
}

final class YuklemeDenetleyicisi {
    public function __construct(private SahteKuyruk $kuyruk, private string $isSinifi = KucukResimIsi::class) {}
    public function yukle(int $genislik): array {
        Gunluk::yeniIstek();
        Gunluk::yola('kucukresim');
        $this->kuyruk->koy(new ($this->isSinifi)($genislik));
        return [201, 'yuklendi'];
    }
}
```

```php
<?php
// test.php — kitaplık yok: iki fonksiyonlu küçük bir doğrulayıcı
declare(strict_types=1);

final class Test {
    public static int $gecen = 0;
    public static int $kalan = 0;

    public static function dogrula(bool $kosul, string $mesaj): void {
        if (!$kosul) {
            throw new RuntimeException($mesaj);
        }
    }

    public static function calistir(string $ad, callable $test): void {
        try {
            $test();
            self::$gecen++;
            $durum = 'gecti';
        } catch (RuntimeException $hata) {
            self::$kalan++;
            $durum = 'kaldi — ' . $hata->getMessage();
        }
        printf("%-28s %s\n", $ad, $durum);
    }

    public static function ozet(): void {
        printf("toplam: %d gecti, %d kaldi\n", self::$gecen, self::$kalan);
    }
}
```

## Özellik Testi: İş Kaydedildi, Koşmadı

`ozellik.php` isteği denetleyiciye verir ve üç şeyi doğrular: yanıt 201 mü, iş kuyruğa girdi mi, iş yanıt sırasında koşmadı mı. Son satır `kucukresim` parçasının sınıfını yazar.

```php
<?php
// ozellik.php — özellik testi: istekten yanıta, sahte kuyrukla
require_once 'uygulama.php';
require_once 'test.php';

$kuyruk = new SahteKuyruk();
$yanit = (new YuklemeDenetleyicisi($kuyruk))->yukle(800);

Test::calistir('yanit 201 doner', fn() => Test::dogrula($yanit[0] === 201, "durum {$yanit[0]}"));
Test::calistir('is kuyruga girdi', fn() => Test::dogrula(count($kuyruk->isler) === 1, 'kuyruk bos'));
Test::calistir('is yanitta calismadi', fn() => Test::dogrula(sinifla('kucukresim') === 'ertelendi', 'sinif ' . sinifla('kucukresim')));
Test::ozet();
printf("kucukresim: %s\n", sinifla('kucukresim'));
```

```
yanit 201 doner              gecti
is kuyruga girdi             gecti
is yanitta calismadi         gecti
toplam: 3 gecti, 0 kaldi
kucukresim: ertelendi
```

Üç test geçti ve iş **ertelendi**: kaydedildi, ama koşmadı. Testin doğruladığı şey tam olarak budur. "İş kuyruğa girdi" cümlesi doğru ve "iş koştu" cümlesi bu testte hiç denenmedi. Sahte kuyruk işi çalıştırmadığı için özellik testi, işin ne yaptığı hakkında bir şey söyleyemez.

## Karşıt: Kuyruk Boşaltılınca İş Koştu

`birim.php` aynı isteği yapar, sonra `isle()` ile kuyruğu boşaltır. Bu kez `kucukresim` **koştu**. İşin sonucu da doğrulanır: genişlik 800 iken sonuç 200 olmalıdır. Son test işi kuyruktan bağımsız, doğrudan sınar; birim testi budur.

```php
<?php
// birim.php — karşıt: kuyruk isle() ile boşaltılınca iş koşar; birim testi işi doğrudan sınar
require_once 'uygulama.php';
require_once 'test.php';

$kuyruk = new SahteKuyruk();
(new YuklemeDenetleyicisi($kuyruk))->yukle(800);
$sonuclar = $kuyruk->isle();

Test::calistir('isle() isi calistirdi', fn() => Test::dogrula(sinifla('kucukresim') === 'koştu', 'sinif ' . sinifla('kucukresim')));
Test::calistir('kucuk resim genisligi', fn() => Test::dogrula($sonuclar === [200], 'sonuc ' . json_encode($sonuclar)));
Test::calistir('is tek basina', fn() => Test::dogrula((new KucukResimIsi(100))->calistir() === 25, 'sonuc yanlis'));
Test::ozet();
printf("kucukresim: %s\n", sinifla('kucukresim'));
```

```
isle() isi calistirdi        gecti
kucuk resim genisligi        gecti
is tek basina                gecti
toplam: 3 gecti, 0 kaldi
kucukresim: koştu
```

Aynı iş, aynı denetleyici, aynı istek: yalnız `isle()` çağrıldığında sınıf ertelendiden koştuya geçti. Yani sınıfı değiştiren isteğin kendisi değil, kuyruğun boşaltılmasıdır. Üçüncü test kuyruğa hiç dokunmaz: `KucukResimIsi` doğrudan kurulur, `calistir()` çağrılır ve sonuç 25 olarak okunur.

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

Sahte kuyruk, işin gövdesini ölçmez. `sinir.php` denetleyiciye bilerek yanlış çalışan `HataliKucukResimIsi` verir. Önce aynı özellik testi, sonra aynı işin birim testi koşar.

```php
<?php
// sinir.php — sahte kuyruk işin gövdesini ölçmez: hatalı iş özellik testini geçer
require_once 'uygulama.php';
require_once 'test.php';

$kuyruk = new SahteKuyruk();
$yanit = (new YuklemeDenetleyicisi($kuyruk, HataliKucukResimIsi::class))->yukle(800);

echo "özellik testi\n";
Test::calistir('yanit 201 doner', fn() => Test::dogrula($yanit[0] === 201, "durum {$yanit[0]}"));
Test::calistir('is kuyruga girdi', fn() => Test::dogrula(count($kuyruk->isler) === 1, 'kuyruk bos'));
Test::calistir('is yanitta calismadi', fn() => Test::dogrula(sinifla('kucukresim') === 'ertelendi', 'sinif yanlis'));

echo "birim testi\n";
$sonuc = (new HataliKucukResimIsi(800))->calistir();
Test::calistir('kucuk resim genisligi', fn() => Test::dogrula($sonuc === 200, "beklenen 200, gelen $sonuc"));
Test::ozet();
```

```
özellik testi
yanit 201 doner              gecti
is kuyruga girdi             gecti
is yanitta calismadi         gecti
birim testi
kucuk resim genisligi        kaldi — beklenen 200, gelen 3200
toplam: 3 gecti, 1 kaldi
```

Özellik testinin üç doğrulaması hatalı işle de geçti, çünkü hiçbiri işin gövdesine bakmıyor: genişlik 800 iken iş 3200 döndürüyor, ama sahte kuyruk onu hiç çalıştırmadı. Hatayı yalnız birim testi yakaladı. Bir özellik testi yolun bağlandığını doğrular; yolun sonunda doğru işin yapıldığını doğrulamaz. İkisi birbirinin yerine geçmez.

Bu ayrımın bir bedeli de vardır. İşi gerçekten koşturan bir özellik testi, kuyruğu `isle()` ile boşaltmak zorunda kalır ve iş artık yanıttan sonra değil, testin içinde koşar. Böyle bir test ertelemenin kendisini ölçmez, yalnız işin sonucunu ölçer. Hangi sorunun sorulduğuna göre ikisinden biri seçilir.

## Özet

- Bu dersin ölçü sınıfı **ertelendi**: sahte kuyrukta `kucukresim` işi kaydedildi (`ertele()`), ama özellik testinde koşmadı.
- Test kitaplığı olmadan iki fonksiyonlu bir doğrulayıcı (`dogrula()` ve `calistir()`) özellik ve birim testlerini yazmaya yetti.
- Karşıt: `isle()` çağrılınca aynı iş **koştu**; sınıfı değiştiren istek değil, kuyruğun boşaltılmasıdır.
- Sınırlayıcı ölçümde bilerek yanlış çalışan iş özellik testini geçti, birim testi 800 yerine 3200 sonucunu yakaladı.
- Özellik testi yolun bağlandığını, birim testi işin doğru yaptığını doğrular; biri ötekinin yerini tutmaz.

## Kurs Kapanışı

Bu kursta 21 derste bir parçanın bir istekte koşup koşmadığını ölçtük: çerçevenin temelleri, görünümler ve veri, uygulama hizmetleri. Her ders kendi ölçü sınıfını ve bir karşıt ölçümünü taşıdı. Aşağıdaki tablo hepsini tek yerde toplar; sınıf ve karşıt hücreleri kaynak dersin özetinden alınmıştır.

| Ders | Ölçülen parça | Sınıf | Karşıt ölçüm |
|---|---|---|---|
| Neden Çerçeve | `kaydet` eylemi, `onbellek` dinleyicisi | koştu | Çekirdek 4 istek 12 parça: 4 koştu, 3 atlandı, 3 ertelendi, 2 bağlanmadı |
| İstek–Yanıt Yaşam Döngüsü | `yanittanSonra()` ile bırakılan iş | ertelendi | Aynı iş eylemde doğrudan çağrılınca koştu |
| Yönlendirme | Gölgelenen `form` rotası | bağlanmadı | Sıra değişince ya da `{id:\d+}` ile daraltılınca `form` koştu |
| Denetleyiciler | Eylem, bağımlılık çözüldü | koştu | Tanımsız `duzenle` eylemi 404, parça bağlanmadı |
| Ara Katmanlar | `dogrulama`, `kaydet` | atlandı | Oturumlu istekte dört parça da koştu |
| Nesne Kabı ve Cepheler | Ezilen `DosyaYazici` kaydı | bağlanmadı | Tek kayıtlı kapta `DosyaYazici` koştu |
| Şablon Motoru | Kaçışlama adımı | koştu | Ham çıktı sözdiziminde kaçışlama bağlanmadı |
| Form ve Doğrulama | Kayıt adımı, geçersiz girdi 422 | atlandı | Geçerli girdide kayıt koştu, 201 |
| Nesne ve Tablo Bağlaması | Kaydetme kancası | koştu | İzin listesinde olmayan alan bağlanmadı |
| İlişkiler | Tembel yüklemede ilişki sorgusu | ertelendi | İstekli yüklemede ilişki listeyle birlikte koştu |
| Göçler ve Tohumlama | Kayıtlı göç, ikinci çalıştırma | atlandı | Yeni eklenen göç koştu |
| Sayfalama ve Kaynak Dönüşümü | Modele eklenen kolonun yanıta ulaşması | bağlanmadı | Dönüşüm tanımına eklenen kolon koştu |
| Kimlik Doğrulama ve Yetkilendirme | `duzenle` eylemi, kapı 403 | atlandı | Yazının sahibinde `duzenle` koştu, 200 |
| Kuyruklar ve İşler | Küçük resim işi | ertelendi | Eşzamanlı sürücüde iş koştu |
| Olaylar ve Dinleyiciler | `dizin` dinleyicisi, ad uyuşmadı | bağlanmadı | Ad uyuşunca dinleyici koştu |
| Bildirimler | Seçilen posta ve veritabanı kanalları | koştu | Seçilmeyen kanallar bağlanmadı |
| Önbellekleme | `hesapla`, ikinci istekte isabet | atlandı | Geçersiz kılmadan sonra `hesapla` yeniden koştu |
| Dosya Depolama | `yukle()` ve `yaz()`, iki disk | koştu | Adı geçmeyen `uzak` disk bağlanmadı, 500 |
| Zamanlanmış Görevler | `rapor` görevi | ertelendi | Saat 60'ta koştu, 30/59/61'de atlandı |
| Yerelleştirme | `en/indirim` metni | bağlanmadı | Anahtar varken istenen dil koştu |
| Test | Sahte kuyruktaki iş | ertelendi | `isle()` çağrılınca iş koştu |

Derslerin dağılımı 6 koştu, 5 ertelendi, 5 atlandı, 5 bağlanmadı olarak okunur. Bu, 21 dersin her birinin kendi ölçü sınıfını sayar. Neden Çerçeve dersindeki çekirdeğin dağılımı ise ayrı bir şeyi sayar: o dersin 12 beklenen parçasından 4 koştu, 3 ertelendi, 3 atlandı, 2 bağlanmadı; yani 4 istekte 4 parça koştu, 8 parça koşmadı.

Tablodaki dağılım, bir uygulamada beklenen çoğu parçanın bir istekte koşmadığını gösterir; yanıt yine de 200 ya da 201 olabilir. Hangi sınıfın hangi yoldan geldiğini bilmek, bir test yazarken neyi doğruladığını bilmeye yarar. Kursun bıraktığı soru, bu uygulamanın kalitesinin ölçülmesidir: kodun biçimi, tekrarı ve hata sınıfları gibi başka bir konu alanı.
