Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
Dizi · 54/64 Dart Türkçe Tutorial
- Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
- Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
- Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
- Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
- Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
- Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
- Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
- Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
- Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
- Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
- Bölüm 3 — Konu 11: `if / else if / else`
- Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
- Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
- Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
- Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
- Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
- Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
- Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
- Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
- Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
- Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
- Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
- Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
- Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
- Bölüm 5 — Konu 26: Recursion (Özyineleme)
- Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
- Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
- Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
- Bölüm 6 — Konu 30: Getter / Setter
- Bölüm 6 — Konu 31: Statik Üyeler (`static`)
- Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
- Bölüm 7 — Konu 33: Metod Override Etme, `@override`
- Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
- Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
- Bölüm 7 — Konu 36: Mixin (`with`)
- Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
- Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
- Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
- Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
- Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
- Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
- Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
- Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
- Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
- Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
- Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
- Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
- Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
- Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
- Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
- Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
- Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
- Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
- Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
- Bölüm 12 — Konu 56: Extension Methods
- Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
- Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
- Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
- Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
- Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
- Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
- Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
- Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
- Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
İçindekiler 11 başlık
Neden Test Yazmalısın?
Şimdiye kadar kodunu test etmek için genelde print() kullanıp çıktıyı elle kontrol ettik. Bu, küçük örnekler için işe yarar ama gerçek bir projede sürdürülebilir değildir — her değişiklikten sonra onlarca print() çıktısını elle kontrol etmek hem yavaş hem hataya açıktır. Unit test (birim test), bu kontrolü otomatikleştirir.
test Paketini Ekleme
Bölüm 12 Konu 53'te öğrendiğimiz gibi, bu bir dev dependency'dir (sadece geliştirme sırasında gerekir, üretim kodunda değil):
dart pub add --dev testİlk Testini Yazma
Dart projelerinde, test dosyaları geleneksel olarak test/ klasöründe, _test.dart uzantısıyla biter:
lib/
matematik.dart
test/
matematik_test.dartlib/matematik.dart:
int topla(int a, int b) => a + b;
int kare(int x) => x * x;test/matematik_test.dart:
import 'package:test/test.dart';
import 'package:benim_projem/matematik.dart';
void main() {
test('topla fonksiyonu iki sayıyı doğru toplamalı', () {
expect(topla(2, 3), equals(5));
});
test('kare fonksiyonu doğru hesaplamalı', () {
expect(kare(4), equals(16));
});
}Yapıyı parçalayalım:
test('açıklama', () { ... })— bir test tanımlar. İlk parametre, testin ne test ettiğini açıklayan bir string; ikinci parametre, testin gerçek kodunu içeren bir fonksiyon.expect(gercekDeger, matcher)— testin kalbi. Elde ettiğin gerçek değeri (topla(2, 3)), beklediğin değerle (equals(5)) karşılaştırır.
Testleri Çalıştırma
dart testBu komut, test/ klasöründeki tüm test dosyalarını bulur ve çalıştırır. Çıktı, her testin geçip geçmediğini gösterir:
00:01 +2: All tests passed!Eğer bir test başarısız olursa:
test('kare fonksiyonu doğru hesaplamalı', () {
expect(kare(4), equals(17)); // bilerek YANLIŞ beklenti
});00:01 +1 -1: kare fonksiyonu doğru hesaplamalı [E]
Expected: <17>
Actual: <16>Test çıktısı, tam olarak neyin beklendiğini ve gerçekte neyin döndüğünü gösterir — bu, hatayı hızlıca anlamanı sağlar.
expect() ve Matcher'lar
import 'package:test/test.dart';
void main() {
test('çeşitli matcher örnekleri', () {
expect(5, equals(5)); // eşitlik kontrolü
expect(5, isNot(equals(6))); // eşit OLMAMA kontrolü
expect(5 > 3, isTrue); // bool kontrolü
expect(null, isNull); // null kontrolü
expect([1, 2, 3], hasLength(3)); // liste uzunluğu
expect([1, 2, 3], contains(2)); // eleman içeriyor mu
expect(() => throw Exception(), throwsException); // hata fırlatıyor mu
});
}Dart'ın test paketi, birçok hazır matcher (eşleştirici) sunar — bunlar, expect()'in ikinci parametresinde kullanılan, farklı türde kontroller yapmanı sağlayan yardımcı araçlardır. equals() en yaygın kullanılanı olsa da, duruma göre isTrue, contains(), throwsException gibi daha spesifik matcher'lar kullanmak, testin niyetini daha net ifade eder.
group() — Testleri Gruplama
import 'package:test/test.dart';
void main() {
group('Matematik fonksiyonları', () {
test('topla doğru çalışmalı', () {
expect(topla(2, 3), equals(5));
});
test('kare doğru çalışmalı', () {
expect(kare(4), equals(16));
});
});
group('String fonksiyonları', () {
test('büyük harfe çevirme çalışmalı', () {
expect("merhaba".toUpperCase(), equals("MERHABA"));
});
});
}group(), ilgili testleri mantıksal olarak bir araya toplar — bu hem test çıktısını daha okunabilir yapar hem de büyük projelerde testleri organize etmeni sağlar (Bölüm 12 Konu 54'te öğrendiğimiz dosya organizasyonuna benzer bir mantık).
setUp() ve tearDown() — Her Testten Önce/Sonra Çalışan Kod
import 'package:test/test.dart';
class SepetHesaplayici {
List<double> urunler = [];
void ekle(double fiyat) => urunler.add(fiyat);
double toplam() => urunler.fold(0, (t, f) => t + f);
}
void main() {
late SepetHesaplayici sepet;
setUp(() {
sepet = SepetHesaplayici(); // HER testten önce çalışır, temiz bir sepet garantiler
});
test('boş sepetin toplamı sıfır olmalı', () {
expect(sepet.toplam(), equals(0));
});
test('ürün eklenince toplam artmalı', () {
sepet.ekle(10);
sepet.ekle(20);
expect(sepet.toplam(), equals(30));
});
}setUp(), her testten önce çalışan bir fonksiyondur — burada her test için temiz, sıfırlanmış bir SepetHesaplayici oluşturuyoruz. Bu önemli çünkü testler birbirinden bağımsız olmalıdır — bir testin, bir öncekinin bıraktığı durumdan etkilenmemesi gerekir. late kullanımını Bölüm 8'de hatırlarsan — burada sepet, setUp() içinde atanacağı için late olarak tanımlandı.
tearDown(), benzer şekilde her testten sonra çalışır — genelde temizlik işlemleri için (örneğin bir test veritabanı bağlantısını kapatmak) kullanılır.
Neden Test Yazmak Önemli? — Somut Faydalar
1. Regresyon (geri kaçma) önleme: Kodunu değiştirdiğinde, testler otomatik olarak eskiden çalışan bir şeyi bozup bozmadığını kontrol eder.
2. Dokümantasyon işlevi: İyi yazılmış testler, bir fonksiyonun nasıl kullanılması gerektiğini ve hangi durumları ele aldığını gösterir — bazen kod yorumlarından bile daha güvenilir bir "dokümantasyon" olabilir.
3. Güvenle refactor etme: Bölüm 7'de öğrendiğimiz gibi, kodun iç yapısını değiştirmek (refactoring) isteyebilirsin — testler varsa, değişiklikten sonra hiçbir şeyi bozmadığından emin olabilirsin.
Hata Durumlarını Test Etme
Bölüm 8'de öğrendiğimiz exception'ları da test edebilirsin:
void yasKontrolEt(int yas) {
if (yas < 0) throw Exception("Yaş negatif olamaz");
}
void main() {
test('negatif yaş exception fırlatmalı', () {
expect(() => yasKontrolEt(-5), throwsException);
});
test('geçerli yaş hata fırlatmamalı', () {
expect(() => yasKontrolEt(25), returnsNormally);
});
}() => yasKontrolEt(-5) — dikkat et, expect()'e fonksiyonu çağırarak değil, bir closure olarak (Bölüm 5'te öğrendiğimiz kavram) veriyoruz. Bunun nedeni, test paketinin fonksiyonu kendisinin çağırıp, hatanın gerçekten fırlatılıp fırlatılmadığını kontrol edebilmesi gerekmesidir — eğer yasKontrolEt(-5) yazsaydık, hata hemen, expect() çağrılmadan önce fırlatılır ve testin kendisi çökerdi.
🎯 Bu Dersten Çıkarılması Gerekenler
- Unit test, kodunun doğru çalıştığını otomatik olarak doğrulamanı sağlar;
print()ile elle kontrol etmenin yerini alır. test('açıklama', () { ... }), bir testi tanımlar;expect(gercekDeger, matcher), testin kalbi olan karşılaştırmayı yapar.group(), ilgili testleri mantıksal olarak bir araya toplar.setUp()/tearDown(), her testten önce/sonra çalışan, testlerin birbirinden bağımsız kalmasını sağlayan kod bloklarıdır.- Hata fırlatan kodu test ederken, fonksiyonu çağırmadan, bir closure olarak
expect()'e vermelisin (throwsExceptionmatcher'ı ile). - Testler, regresyon önleme, dokümantasyon ve güvenli refactoring için değerlidir.
📝 Ödevler
- [ ] Basit bir fonksiyon için (örn.
topla,kare)testpaketiyle en az 3 unit test yaz,dart testile çalıştır. - [ ] Bilerek başarısız olacak bir test yaz (yanlış bir
expectbeklentisiyle), hata çıktısını oku ve yorumla. - [ ]
group()kullanarak, ilgili testleri (örn. tüm matematik fonksiyonları) bir arada grupla. - [ ]
setUp()kullanarak, her testten önce sıfırlanan bir nesne (örn. bir sayaç veya sepet sınıfı) ile en az 2 bağımsız test yaz. - [ ]
throwsExceptionmatcher'ını kullanarak, hata fırlatması beklenen bir fonksiyonu test et;returnsNormallyile de normal çalışan bir durumu test et.
Sıradaki konu: Bölüm 12 — Konu 56: Extension Methods