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 Test | Widget Test | Integration Test | |
|---|---|---|---|
| Kecepatan | Milidetik | Detik | Menit |
| Isolasi | Penuh | Sebagian | Minimal |
| Butuh device | Tidak | Tidak | Ya |
| Rentan flaky | Tidak | Jarang | Sering |
| Biaya tulis | Rendah | Sedang | Tinggi |
| Cocok untuk | Logika bisnis | UI & interaksi | Alur 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 →