Overview #

Testing adalah investasi, bukan beban. Kode yang ditest dengan baik lebih mudah direfactor, lebih aman untuk dikembangkan dalam tim, dan menemukan bug jauh lebih awal — saat biayanya masih murah. Flutter menyediakan ekosistem testing yang lengkap dari unit test hingga integration test end-to-end, dan memahami kapan menggunakan masing-masing adalah keterampilan yang sama pentingnya dengan kemampuan menulis test itu sendiri.

Piramida Testing #

Piramida testing adalah panduan visual tentang berapa banyak test dari setiap jenis yang idealnya kamu miliki:

                    ╱‾‾‾‾‾‾‾‾‾‾‾‾‾╲
                   ╱  Integration  ╲    sedikit, lambat, mahal
                  ╱   (E2E, Patrol) ╲
                 ╱‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾╲
                ╱     Widget Test     ╲  sedang
               ╱    (flutter_test)     ╲
              ╱‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾╲
             ╱         Unit Test         ╲  banyak, cepat, murah
            ╱    (dart:test, mocktail)    ╲
           ╱‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾╲

Unit Test      : 70% dari semua test
Widget Test    : 20%
Integration    : 10%

Semakin ke atas, test semakin lambat, semakin mahal ditulis, semakin rentan terhadap flakiness (kadang gagal bukan karena bug). Semakin ke bawah, test semakin cepat, lebih terisolasi, dan lebih mudah di-maintain.


Tiga Layer Testing di Flutter #

Unit Test #

Test paling kecil dan paling cepat. Menguji satu unit logika secara terisolasi — fungsi, class, repository, atau notifier — tanpa melibatkan UI, jaringan, atau database nyata.

Yang ditest:
  ✓ Fungsi pure (kalkulasi, validasi, transformasi data)
  ✓ Repository dan data source (dengan mock)
  ✓ Notifier/Bloc/Cubit (logika state)
  ✓ Model dan parsing JSON
  ✓ Utility classes

Tools:
  dart:test (built-in)
  mocktail   -- mock dependency tanpa code generation
  mockito    -- mock dengan code generation

Widget Test #

Menguji satu widget atau sekumpulan widget kecil — bagaimana ia di-render, bagaimana ia merespons interaksi, apakah ia menampilkan konten yang benar. Berjalan di lingkungan Flutter yang di-emulasi, lebih cepat dari integration test tapi lebih lambat dari unit test.

Yang ditest:
  ✓ Tampilan widget untuk berbagai state (loading, error, data)
  ✓ Interaksi: tap, input teks, scroll
  ✓ Navigasi antar halaman dalam satu flow
  ✓ Golden test -- snapshot visual widget

Tools:
  flutter_test (built-in)
  WidgetTester, Finder, Matcher
  patrol_finders -- finder yang lebih ringkas

Integration Test (End-to-End) #

Menguji keseluruhan aplikasi yang berjalan di device atau emulator nyata — dari login, navigasi, hingga interaksi dengan sistem operasi (izin, notifikasi, WebView). Paling lambat, paling mahal, paling mirip dengan pengguna nyata.

Yang ditest:
  ✓ Alur utama pengguna dari awal sampai akhir
  ✓ Interaksi dengan dialog izin native (kamera, lokasi, notifikasi)
  ✓ Integrasi dengan fitur OS (notifikasi, deep link)
  ✓ Happy path untuk fitur kritis

Tools:
  patrol         -- framework E2E modern dengan native automation
  integration_test -- built-in Flutter (tanpa native automation)

Perbandingan Cepat #

Unit TestWidget TestIntegration Test
KecepatanMilidetikDetikMenit
IsolasiPenuhSebagianMinimal
Butuh deviceTidakTidakYa
Rentan flakyTidakJarangSering
Biaya tulisRendahSedangTinggi
Cocok untukLogika bisnisUI & interaksiAlur kritis

Struktur Folder Test #

lib/
  features/
    produk/
      data/
        produk_repository_impl.dart
      domain/
        produk_repository.dart
      presentation/
        produk_notifier.dart
        produk_screen.dart

test/                              ← unit & widget test
  features/
    produk/
      data/
        produk_repository_impl_test.dart
      presentation/
        produk_notifier_test.dart
        produk_screen_test.dart
  helpers/
    mock_repositories.dart         ← mock yang dipakai bersama
    pump_app.dart                  ← helper pumpWidget dengan wrapper

integration_test/                  ← integration test (Patrol)
  flows/
    login_flow_test.dart
    produk_flow_test.dart
  helpers/
    app_helper.dart

Konvensi: nama file test selalu diakhiri dengan _test.dart, dan path-nya mencerminkan path file yang ditest di lib/.


Filosofi — Apa yang Perlu Ditest? #

TEST INI:
  ✓ Logika bisnis yang kompleks (kalkulasi, validasi, transformasi)
  ✓ Edge case yang sulit ditest secara manual
  ✓ Bug yang pernah terjadi -- tulis test untuk memastikan tidak terulang
  ✓ Alur kritis pengguna (login, checkout, pembayaran)
  ✓ State transitions di notifier (loading → error → success)

JANGAN TERLALU FOKUS PADA:
  ✗ Getter dan setter yang trivial
  ✗ Kode yang hanya meneruskan (pass-through) tanpa logika
  ✗ Detail implementasi yang sering berubah
  ✗ Kode third-party yang sudah ditest oleh maintainernya
  ✗ 100% coverage -- coverage tinggi tidak menjamin kualitas

Fokus pada nilai test, bukan angka coverage. Test yang menangkap bug nyata jauh lebih berharga dari test yang hanya meningkatkan angka coverage.


Perintah Dasar #

# Jalankan semua unit dan widget test
flutter test

# Jalankan file test tertentu
flutter test test/features/produk/produk_notifier_test.dart

# Jalankan dengan coverage report
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html

# Jalankan hanya test dengan nama tertentu
flutter test --name "berhasil memuat produk"

# Jalankan integration test dengan Patrol
patrol test -t integration_test/flows/login_flow_test.dart

# Update golden files
flutter test --update-goldens

TDD — Test Driven Development #

TDD adalah pendekatan di mana kamu menulis test sebelum menulis implementasi. Siklus TDD terdiri dari tiga langkah yang diulang:

1. RED   -- tulis test yang gagal (karena implementasi belum ada)
2. GREEN -- tulis implementasi seminimal mungkin agar test lulus
3. REFACTOR -- perbaiki kode tanpa mengubah perilaku (test tetap hijau)

Manfaat TDD:
  ✓ Memaksa kamu berpikir tentang API sebelum implementasi
  ✓ Setiap kode pasti tercover karena ditulis untuk lulus test
  ✓ Refactoring lebih aman -- test adalah jaring pengaman
  ✓ Desain yang lebih bersih -- kode yang sulit ditest biasanya desainnya buruk

Kapan TDD cocok:
  ✓ Logika bisnis yang jelas spesifikasinya
  ✓ Bug fixing -- tulis test yang repro bug, baru fix
  ✗ Eksplorasi UI -- terlalu cepat berubah untuk TDD
  ✗ Prototyping -- overhead TDD tidak sebanding untuk throwaway code

Ringkasan #

  • Piramida testing: banyak unit test (70%), sedang widget test (20%), sedikit integration test (10%). Semakin ke atas semakin lambat dan mahal.
  • Unit test untuk logika bisnis terisolasi — notifier, repository, model, fungsi pure. Cepat, tidak butuh device.
  • Widget test untuk tampilan dan interaksi UI — render state, tap, input, navigasi. Berjalan di emulator ringan tanpa device nyata.
  • Integration test untuk alur kritis end-to-end — login, checkout, interaksi native. Butuh device atau emulator nyata, gunakan Patrol untuk bisa berinteraksi dengan dialog izin native.
  • Struktur folder test mencerminkan struktur lib/ — mudah menemukan test yang relevan untuk setiap file.
  • Fokus pada nilai test, bukan angka coverage — test yang menangkap bug nyata lebih berharga dari 100% coverage tanpa makna.
  • TDD (Red → Green → Refactor) cocok untuk logika bisnis yang spesifikasinya jelas, kurang cocok untuk eksplorasi UI.

← Sebelumnya: Local Storage Best Practice   Berikutnya: Unit Test →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact