---
title: 'Test Türleri'
source: 'https://academia.sh/tr/kurslar/flutter/test-turleri'
course: Flutter
language: tr
updated: '2026-10-21T05:02:53+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Birim, bileşen ve tümleşim testinin neyi sınadığını ayırır'
    - 'Beklenen boyutlanmayı ölçülen kayıtla karşılaştıran bir denetim yazar'
    - 'Geçen ve kalan bir denetimin çıktısını okur'
    - 'Aynı ekranın iki test yüzeyinde farklı boyutlanabileceğini gösterir'
---

# Test Türleri

Bir test, beklenen boyutlanmayı ölçülen kayıtla karşılaştırır. Ders aynı ekranı iki test yüzeyinde ölçer ve testin neyi göremediğini gösterir.

Önceki derslerde uygulamanın yapısı kuruldu: bileşenler ağaca dizildi, durum ve platform kanalı bağlandı. Elde çalışan bir ekran var, ama çalıştığını bir kişinin ona bakarak söylemesi yetmez. Bu ders, bir ekranın beklenen davranışını yazıya dökmenin ve her değişiklikte makineye denetletmenin yolunu anlatır. Konu test türleridir; kursun ekseni olan "bu bileşenin genişliğini kim belirliyor" sorusu da bu derste bir test beklentisine dönüşür.

## Üç Test Türü

Testler neyi bir araya getirdiklerine göre ayrılır. **Birim testi** tek bir işlevi ya da sınıfı alır, çevresinden yalıtır ve girdiye karşılık çıktıyı sınar. Ağaç, ekran ve platform yoktur; test hızlıdır ve neden kaldığını söylemesi kolaydır. **Bileşen testi** tek bir bileşeni ya da küçük bir bileşen ağacını gerçek çerçeveyle kurar, ama gerçek bir aygıt açmaz. Test, ağacı kurar, bir dokunmayı taklit eder, ardından ağaçta bir bileşenin varlığını ya da değerini sorgular. **Tümleşim testi** uygulamanın tamamını, ya da büyük bir bölümünü, bir aygıtta ya da emülatörde çalıştırır ve kullanıcının izleyeceği yolu baştan sona dolaşır.

Üçü arasındaki değiş tokuş basittir. Birim testi çabuk koşar ama bileşenlerin birbirine nasıl oturduğunu görmez. Tümleşim testi her şeyi görür ama yavaştır, kurulumu ağırdır ve kaldığında hatanın nerede olduğunu söylemez. Bileşen testi ikisinin arasında durur: ağacı görür, platformu görmez. Bu kursun ekseni olan boyutlanma da tam bu orta katmanda yaşar. Bir bileşenin genişliği, ebeveyninin kısıtına ve kendi isteğine bağlıdır; bu iki girdi bir ağaç kurulduğunda ortaya çıkar, tek bir işlevi çağırmakla ya da uygulamayı baştan sona dolaşmakla çıkmaz.

## Ölçülen Katman

Kursun her dersi aynı düz Dart modelini kullanır. Model bir bileşen ağacına ekran genişliğini verir ve her düğümün genişliğini kimin belirlediğini dört adla kaydeder: dayatılan, seçilen, kırpılan, esnetilen. Aşağıdaki dosya bu dersin çekirdeğidir; çıktı üretmez, ölçülen bloklar onu içe alır.

```dart
// sizing.dart — bir bilesenin genisligini kimin belirledigini siniflandirir
const sizings = ['dayatilan', 'secilen', 'kirpilan', 'esnetilen'];

class Limit {
  final int min;
  final int? max; // null: sinirsiz
  const Limit(this.min, this.max);

  bool get tight => max == min;
  Limit loosen() => Limit(0, max);
  Limit deflate(int by) => Limit(_floor(min - by), max == null ? null : _floor(max! - by));
  String bounds() => '$min..${max ?? 'sonsuz'}';
}

int _floor(int value) => value < 0 ? 0 : value;

class Part {
  final String kind;
  final String name;
  final int? width; // null: alabildigi kadar genis
  final List<Part> children;
  final int factor;
  final bool tightFlex;
  const Part(this.kind, this.name, this.width, this.children,
      {this.factor = 0, this.tightFlex = true});
}

Part leaf(String name, int width) => Part('leaf', name, width, const []);
Part fill(String name) => Part('leaf', name, null, const []);
Part pad(String name, int padding, Part child) => Part('pad', name, padding, [child]);
Part center(String name, Part child) => Part('center', name, null, [child]);
Part fixed(String name, int width, Part child) => Part('fixed', name, width, [child]);
Part column(String name, List<Part> children) => Part('column', name, null, children);
Part row(String name, List<Part> children) => Part('row', name, null, children);
Part flex(String name, int factor, Part child, {bool tight = true}) =>
    Part('flex', name, null, [child], factor: factor, tightFlex: tight);

class Sizing {
  final String sizing;
  final String path;
  final int width;
  final Limit limit;
  const Sizing(this.sizing, this.path, this.width, this.limit);
}

Sizing classify(String path, int? desire, Limit limit) {
  if (limit.tight) return Sizing('dayatilan', path, limit.min, limit);
  if (desire == null) {
    return limit.max == null
        ? Sizing('kirpilan', path, limit.min, limit)
        : Sizing('esnetilen', path, limit.max!, limit);
  }
  if (desire < limit.min) return Sizing('kirpilan', path, limit.min, limit);
  if (limit.max != null && desire > limit.max!) {
    return Sizing('kirpilan', path, limit.max!, limit);
  }
  return Sizing('secilen', path, desire, limit);
}

int layout(Part part, Limit limit, List<Sizing> log, [String path = '']) {
  final here = '$path/${part.name}';
  final below = <Sizing>[];
  if (part.kind == 'fixed') {
    final own = classify(here, part.width, limit);
    layout(part.children.first, Limit(own.width, own.width), below, here);
    log..add(own)..addAll(below);
    return own.width;
  }
  int? desire;
  switch (part.kind) {
    case 'leaf':
      desire = part.width;
    case 'pad':
      final space = 2 * part.width!;
      desire = layout(part.children.first, limit.deflate(space), below, here) + space;
    case 'center':
      final inner = layout(part.children.first, limit.loosen(), below, here);
      desire = limit.max == null ? inner : null;
    case 'flex':
      desire = layout(part.children.first, limit, below, here);
    case 'column':
      var widest = 0;
      for (final child in part.children) {
        final width = layout(child, limit.loosen(), below, here);
        if (width > widest) widest = width;
      }
      desire = widest;
    case 'row':
      final logs = [for (final _ in part.children) <Sizing>[]];
      final bounded = limit.max != null;
      var used = 0;
      var factors = 0;
      for (var i = 0; i < part.children.length; i++) {
        final child = part.children[i];
        if (child.kind == 'flex' && bounded) {
          factors += child.factor;
        } else {
          used += layout(child, const Limit(0, null), logs[i], here);
        }
      }
      final left = bounded ? _floor(limit.max! - used) : 0;
      var given = 0;
      var seen = 0;
      for (var i = 0; i < part.children.length; i++) {
        final child = part.children[i];
        if (child.kind != 'flex' || !bounded) continue;
        seen += child.factor;
        final share = (seen == factors ? left : left * seen ~/ factors) - given;
        given += share;
        final room = child.tightFlex ? Limit(share, share) : Limit(0, share);
        used += layout(child, room, logs[i], here);
      }
      for (final entries in logs) {
        below.addAll(entries);
      }
      desire = bounded ? null : used;
  }
  final own = classify(here, desire, limit);
  log..add(own)..addAll(below);
  return own.width;
}

List<Sizing> measure(Part root, int screen) {
  final log = <Sizing>[];
  layout(root, Limit(screen, screen), log);
  return log;
}

void printSizes(List<Sizing> log) {
  for (final entry in log) {
    print('${entry.sizing}: ${entry.path} genislik=${entry.width} kisit=${entry.limit.bounds()}');
  }
}

void countSizings(List<Sizing> log) {
  for (final name in sizings) {
    print('$name: ${log.where((entry) => entry.sizing == name).length}');
  }
  print('ebeveyn: ${log.where((entry) => entry.sizing != 'secilen').length}');
}
```

## Birim Testi: Ağaç Yok

Aşağıdaki parça bir birim testidir. Bir sayaç işlevini sınar ve çerçeveden yalnızca bir test tanımı ile bir beklenti ister. Çerçeve kodu olduğu için bu parça koşmaz; ders ona çıktı iddia etmez.

```dart
// Platform kodu: birim testi
void main() {
  test('sayac bir artar', () {
    final counter = Counter();
    counter.increment();
    expect(counter.value, 1);
  });
}
```

Testin üç parçası vardır: kurulum, eylem, beklenti. `Counter` kurulur, `increment` çağrılır ve değerin bir olduğu beklenir. Hiçbir yerde genişlik, kısıt ya da ekran geçmez. Birim testi bu kursun sorusuna cevap veremez; çünkü soru, bileşenin bir ebeveynin içinde durmasını gerektirir.

## Bileşen Testi: Ağaç Var, Platform Yok

Bileşen testi ağacı kurar. Çerçeve bunun için bir test ortamı sunar: bileşen sahte bir ekrana yerleştirilir, bir kare çizilir ve testin sorguları ağacı gezer.

```dart
// Platform kodu: bileşen testi
void main() {
  testWidgets('baslik gorunur', (tester) async {
    await tester.pumpWidget(const ExampleScreen());
    expect(find.text('Rapor'), findsOneWidget);
    expect(tester.getSize(find.byType(Chart)).width, 300);
  });
}
```

Son satır önemlidir. Test bir bileşenin genişliğini sorar ve 300 bekler. Bu beklenti, bileşenin kendi isteği olan 300 birimi kimsenin değiştirmeyeceğini varsayar. Testin çalıştığı ekran genişliği bu varsayımı taşır, ama test bunu yazmaz. Test yüzeyi, yani testin sahte ekranının genişliği, bileşen testinin gizli girdisidir.

Aşağıdaki bölümler bu girdiyi görünür kılar. Modelde test, ölçülen kayıt listesinden bir yolu bulup boyutlanma adını beklenenle karşılaştıran küçük bir işlevdir.

## Test Edilen Ekran

Denetlenecek ekran iki bileşenli bir sütundur: 120 birim isteyen bir başlık ve genişliği ayarlanabilen bir grafik. Ekran, grafiğin istediği genişliği alan bir işlevle kurulur; böylece ağaç duruma bağlıdır ve her ölçüm kendi ağacını kurar. Ağaç kuran işlev model dosyasında durur.

```dart
// screen_model.dart — test edilen ekranin bilesen agaci
import 'sizing.dart';

Part buildScreen(int chartWant) => pad('Screen', 16, column('Body', [
      leaf('Title', 120),
      leaf('Chart', chartWant),
    ]));
```

Beklentiyi karşılaştıran işlev de ayrı bir dosyada durur. Ölçülen kayıt listesinden yolu arar; yol varsa boyutlanma adını beklenenle karşılaştırır, yoksa bunu söyler. Beklenen değer `printSizes`'a verilmez; çıktıyı yalnızca bu işlev yazar.

```dart
// check_sizing.dart — beklenen boyutlanmayi olculen kayitla karsilastirir
import 'sizing.dart';

void checkSizing(List<Sizing> log, String path, String expected) {
  final found = log.where((entry) => entry.path == path).toList();
  if (found.isEmpty) {
    print('KALDI: $path kayit yok');
    return;
  }
  final actual = found.first.sizing;
  if (actual == expected) {
    print('GECTI: $path $expected');
  } else {
    print('KALDI: $path beklenen=$expected olculen=$actual');
  }
}
```

## 400 Birimlik Yüzeyde Geçen Test

Önce ekran 400 birimlik bir test yüzeyine konur. Grafik 300 birim ister ve test grafiğin isteğini seçilen olarak bekler. Aşağıdaki blok tüm kaydı basar, ardından denetimi koşar.

```dart
// main.dart
import 'sizing.dart';
import 'screen_model.dart';
import 'check_sizing.dart';

void main() {
  final log = measure(buildScreen(300), 400);
  printSizes(log);
  checkSizing(log, '/Screen/Body/Chart', 'secilen');
}
```

```text
dayatilan: /Screen genislik=400 kisit=400..400
dayatilan: /Screen/Body genislik=368 kisit=368..368
secilen: /Screen/Body/Title genislik=120 kisit=0..368
secilen: /Screen/Body/Chart genislik=300 kisit=0..368
GECTI: /Screen/Body/Chart secilen
```

Kök sıkı ekran kısıtını alır, iç boşluk iki yandan 16'şar birim düşer ve gövde 368 birimlik sıkı kısıt görür. Sütun kısıtı gevşetir; grafiğin 300 birimlik isteği 0..368 aralığına sığar. Grafik genişliğini kendi seçmiştir, test geçer.

## 320 Birimlik Yüzeyde Kalan Test

Aynı ekran, aynı beklenti, yalnızca test yüzeyi 320 birim. Yeni bir geliştirici aynı testi daha dar bir aygıt için de koşturmak ister; kodda hiçbir şey değişmez.

```dart
// main.dart
import 'sizing.dart';
import 'screen_model.dart';
import 'check_sizing.dart';

void main() {
  final log = measure(buildScreen(300), 320);
  printSizes(log);
  checkSizing(log, '/Screen/Body/Chart', 'secilen');
}
```

```text
dayatilan: /Screen genislik=320 kisit=320..320
dayatilan: /Screen/Body genislik=288 kisit=288..288
secilen: /Screen/Body/Title genislik=120 kisit=0..288
kirpilan: /Screen/Body/Chart genislik=288 kisit=0..288
KALDI: /Screen/Body/Chart beklenen=secilen olculen=kirpilan
```

Gövde artık 288 birimlik sıkı kısıt görür. Grafik 300 ister, ama izin verilen en çok 288'dir; çıkan genişlik 288 ve boyutlanma kırpılan. Beklenti "seçilen" idi, test kalır. Kalan test bir hata bulmuştur: ekran dar yüzeyde grafiğini isteğinden küçük gösteriyor. Hatanın nerede olduğu da çıktıdan okunur, çünkü `beklenen` ve `olculen` alanları yan yana durur.

Burada iki yol vardır ve hangisinin seçileceği bir tasarım kararıdır. Ekran dar yüzeyde grafiği küçültebilir; bu davranış istenen davranışsa beklenti değişir. Ya da ekran grafiği kaydırmalı bir alana alır; bu durumda ağaç değişir ve test yeniden geçer. Test hangisinin doğru olduğunu söylemez, yalnızca iki durumun farkını görünür kılar.

## Beklentiyi Düzeltmek

Dar yüzeyde kırpılmak istenen davranışsa beklenti buna göre yazılır. Aşağıdaki blok iki beklentiyi birlikte sınar. Birincisi kırpılanı doğru bekler. İkincisi ağaçta hiç bulunmayan bir düğümü sorar.

```dart
// main.dart
import 'sizing.dart';
import 'screen_model.dart';
import 'check_sizing.dart';

void main() {
  final log = measure(buildScreen(300), 320);
  checkSizing(log, '/Screen/Body/Chart', 'kirpilan');
  checkSizing(log, '/Screen/Body/Legend', 'secilen');
}
```

```text
GECTI: /Screen/Body/Chart kirpilan
KALDI: /Screen/Body/Legend kayit yok
```

İkinci satır önemli bir kuralı gösterir: ağaca girmeyen bir düğüm için kayıt yoktur. Bir testin yazıp da hiçbir şeyi sınamadığı sessiz bir durum buradan doğar. Düğümün adını yanlış yazan bir test, "seçilen" yerine "kayıt yok" ile kalır; bu yüzden denetim işlevi bulunmayan yolu geçmiş saymaz, kalmış sayar.

## Tümleşim Testi Neyi Ekler

Bileşen testinin test yüzeyi modeldeki ekran genişliğidir; bir sayıdır ve testin yazarı onu seçer. Tümleşim testi gerçek bir aygıtta koşar ve aygıtın kendi ekran genişliğini, yazı tipi ölçüsünü, güvenli alan payını kullanır. Aynı ekran iki farklı aygıtta iki farklı genişlikle çalışır; tümleşim testi bunlardan hangisinde koşuyorsa onu görür.

Bu, bileşen testinin yerini tümleşim testinin almadığı anlamına gelir. Bileşen testi yüzeyi bilerek seçer: 400 ve 320 gibi iki sayı, iki sınıfı temsil eder. Tümleşim testi yüzeyi aygıttan alır ve elinin altındaki aygıt neyse onu sınar. İkisini birlikte koşturmak, sayısı az ama anlamlı yüzeylerde ağacı, birkaç gerçek aygıtta ise uygulamanın bütününü denetler.

## Yaygın Yanılgı

**Testler geçiyorsa ekran her koşulda doğrudur.** Böyle değildir. Bu dersin iki ölçümü bunu gösterir: 400 birimde geçen test, 320 birimde kalır. Test aynıdır, ekran aynıdır; değişen yüzeydir. Bir testin geçmesi, ekranın her koşulda doğru olduğunu göstermez; yalnızca testin seçtiği koşulda doğru olduğunu gösterir.

Bu soru M12/K04'teki soruyla karıştırılmamalı. Orada bir testin sorusu "bu parça bu istekte koştu mu" idi ve cevap kapsamaydı. Burada soru, koşan bir bileşenin genişliğini kimin belirlediğidir. Ölçülen her düğüm düzene girmiştir, yani koşmuştur; boyutlanma yalnız düzene giren düğümü sınıflar ve koşmuş olmak doğru boyutlanmış olmak demek değildir.

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

Son ölçüm testin neyi göremediğini gösterir. Test yüzeyi ekranın bileşen ağacına girdi olarak verilir; test bu girdiyi değiştiremez ve girdinin değişince ne olacağını da önceden bilemez. Aşağıdaki blok 320 birimlik yüzeyde yalnızca grafiğin kaydına bakar ve ardından tüm kaydın sayımını verir.

```dart
// main.dart
import 'sizing.dart';
import 'screen_model.dart';

void main() {
  final log = measure(buildScreen(300), 320);
  printSizes(log.where((entry) => entry.path == '/Screen/Body/Chart').toList());
  countSizings(log);
}
```

```text
kirpilan: /Screen/Body/Chart genislik=288 kisit=0..288
dayatilan: 2
secilen: 1
kirpilan: 1
esnetilen: 0
ebeveyn: 3
```

Odak düğüm `/Screen/Body/Chart`. 320 birimlik yüzeyde grafik kırpılan çıktı; dört kaydın üçünde genişliği bileşenin kendisi değil ebeveyn ya da aralık belirliyor. Başlık hâlâ seçilen, ama grafik artık isteğini yerine getiremiyor. Dersin boyutlanması bu yüzden kırpılandır: dar test yüzeyi, grafiğin isteğinin aralığa sığmadığı yerdir.

**KY1.** Model, bu boyutlanmanın kuralını kurar; model çerçevenin kendisi değildir. Modelde bir test, ölçülen kayıt listesinden bir yolu bulup boyutlanma adını karşılaştırır. Gerçek bir bileşen testi bir bileşenin boyutunu aygıtın gerçek ölçüleriyle sorgular ve yazı tipi ölçüsü gibi modelde bulunmayan girdilerden etkilenir. Modelde ölçülen beklentiler gerçek bir testin sorgularıyla bire bir örtüşmez.

## Özet

- Bu dersin boyutlanması: kırpılan. Dar test yüzeyinde `/Screen/Body/Chart` isteğinin altında kaldı ve beklenen "seçilen" bu yüzden tutmadı.
- Birim testi bir işlevi yalıtır, bileşen testi bir ağacı kurar, tümleşim testi uygulamayı bir aygıtta baştan sona dolaşır.
- Boyutlanma bir ağaç özelliğidir; bu yüzden birim testi onu göremez, bileşen testi test yüzeyini seçerek görür.
- `checkSizing` beklenen adı ölçülen kayıtla karşılaştırır ve `GECTI` ya da `KALDI` verir; ağaçta olmayan bir yol geçmiş sayılmaz.
- Aynı ekran 400 birimde geçer, 320 birimde kalır: test yüzeyi bileşen testinin gizli girdisidir.

## Sonraki Adım

Bir test kaldığında hatanın nerede olduğunu söylemek bir şeydir, hatanın neden olduğunu görmek başka. Sıradaki ders, `Geliştirici Araçları`, ağacı çalışırken inceleyen araçları ele alır ve bir bileşenin aldığı kısıtı, genişliği ve çıktının hangi bileşenden geldiğini gösterir. Konunun üçüncü dersi `Başarım Ayarı` ise ağaçtaki gereksiz değişiklikleri azaltmayı anlatır.
