Dart-da test yazmaq
Dart-da testlər package:test ilə yazılır, test/ qovluğunda saxlanılır və fayl adları _test.dart ilə bitir — dart test məhz bu şablona baxır.
Quruluş dörd elementdən ibarətdir:
test('...', () { ... })— bir test halı; adı davranışı təsvir etməlidir ("boş səbətin cəmi sıfırdır"), metod adını yox ("testTotal").group('...', () { ... })— məntiqi qruplaşdırma; report-da adlar birləşir və uğursuz testi tapmaq asanlaşır.setUp/tearDown— hər testdən əvvəl/sonra işləyir. Test obyektlərini burada yaratmaq izolyasiyanın əsasıdır: hər test təmiz vəziyyətlə başlayır.setUpAll/tearDownAll— qrup üzrə bir dəfə. Bahalı və dəyişməyən resurslar üçün (məs. bir dəfə fayl oxumaq). Burada dəyişkən vəziyyət saxlamaq testlər arası sızmanın klassik səbəbidir.
tearDown test fail olanda da icra olunur. Alternativ və çox vaxt daha səliqəli yol — addTearDown(...): cleanup-ı setup ilə eyni sətirdə qeyd edirsən, unutmaq ehtimalı azalır.
| API / matcher | Nə yoxlayır | Nümunə |
|---|---|---|
| `equals(x)` (və ya sadəcə `x`) | `==` bərabərliyi; kolleksiyalar üçün isə **elementbəelement** müqayisə edir | `expect(cart.total, 500)` |
| `isA<T>()` | Tipi; `.having(...)` ilə sahələri də yoxlaya bilər | `expect(e, isA<PaymentDeclined>().having((x) => x.code, 'code', '402'))` |
| `throwsA(matcher)` | Funksiyanın xəta atmasını — arqument **çağırış deyil, closure** olmalıdır | `expect(() => parse('x'), throwsA(isA<FormatException>()))` |
| `completion(matcher)` | Future-un uğurla tamamlanmasını və nəticəsini | `await expectLater(fetchAge(), completion(equals(42)))` |
| `emitsInOrder([...])` | Stream-in verdiyi elementlərin ardıcıllığını; `emitsError`, `emitsDone`, `neverEmits` ilə birlikdə | `await expectLater(s, emitsInOrder([1, 2, emitsDone]))` |
| `predicate((x) => ...)` | İxtiyari şərti — hazır matcher olmayanda | `expect(user, predicate<User>((u) => u.age >= 18, 'is adult'))` |
| `closeTo(v, delta)` | `double` müqayisəsini dözümlə — `0.1 + 0.2 == 0.3` yalandır | `expect(rate, closeTo(0.3, 1e-9))` |
| `expectLater(...)` | Asinxron matcher-lər üçün `expect`-in Future qaytaran versiyası — **mütləq `await`** | `await expectLater(stream, emits(1))` |
Asinxron testlərin bir qaydası var: heç nəyi await-siz buraxma. Test funksiyasını async et və hər asinxron gözləntini await ilə yaz. await expectLater(...) əvəzinə expectLater(...) yazsan, test yoxlama tamamlanmamış bitir və yalançı yaşıl olur — bu, real layihələrdə ən çox itirilən vaxtdır.
Kodu test edilə bilən etmək test yazmaqdan vacibdir. Üç praktik qayda:
- Constructor injection: asılılıq (HTTP client, repository, saat, random) parametr kimi gəlsin, funksiyanın içində
new-lanmasın. Yalnız bu halda testdə əvəzləyə bilərsən. - Statik singleton-lardan qaç:
Db.instancetesti izolyasiyadan məhrum edir və testlərin sırasından asılı edir. - Zamanı və təsadüfü inyeksiya et:
DateTime.now()vəRandom()funksiyanın içində olanda test flaky olur;DateTime Function() nowvəRandom seedparametrləri kifayətdir.
Fake, yoxsa mock? Fake — interfeysin işlək, sadələşdirilmiş implementasiyası (yaddaşdakı repository). Mock — çağırışları qeyd edən və verilmiş cavabları qaytaran obyekt (adətən package:mockito və ya mocktail). Default olaraq fake seç: o, real davranışı saxlayır və refaktorda sınmır. Mock isə qarşılıqlı əlaqəni yoxlamaq lazım olanda dəyərlidir: "analitika hadisəsi dəqiq bir dəfə göndərildimi".
Interview tövsiyəsi: "Coverage neçə faiz olmalıdır?" sualı tələdir. Rəqəm demə — meyar de. Yaxşı cavab: "dart test --coverage=coverage ilə ölçürük, sonra package:coverage ilə LCOV-a çeviririk. Amma coverage yalnız hansı sətirlərin icra olunduğunu göstərir, onların yoxlanıldığını yox — assertion-suz test də coverage yaradır. Ona görə mən mütləq rəqəm yox, iki şey izləyirəm: yeni kodun coverage-i aşağı düşməsin və kritik domain məntiqi (qiymət hesablama, validasiya, state maşını) faktiki olaraq testlə örtülü olsun." Bu cavab "80%" deməkdən qat-qat güclüdür.
📚 Mənbələr və sənədlər
- dart testrəsmidart.dev
- test paketipub.dev