Halo Developer! 👋

Dua artikel terakhir kita sudah bahas Unit Testing (#35) dan Widget Testing (#36). Sekarang kita tutup seri testing dengan level tertinggi: Integration Testing — uji alur lengkap aplikasi di emulator atau device nyata, dari layar satu ke layar lainnya. Ini perbedaan terbesarnya: unit test ngetes fungsi, widget test ngetes satu layar, integration test ngetes seluruh perjalanan user.

Bayangkan kamu sudah test drive mobil di garasi (#36), sekarang saatnya nyetir di jalan raya sungguhan — lengkap dengan lampu merah, polisi tidur, dan pengguna jalan lain. 🚗💨


Apa Itu Integration Testing?

Integration test menjalankan aplikasi Flutter secara utuh di device/emulator, lalu mensimulasikan interaksi user dari awal sampai akhir. Bedanya dengan widget test:

Aspek Widget Test Integration Test
Environment Headless (tanpa device) Device/emulator nyata
Kecepatan ⚡ Detik 🐢 Beberapa menit
Scope Satu widget/screen Flow antar screen
Data Wajib mock API/database sungguhan
Cocok untuk Verifikasi UI cepat End-to-end user journey

Integration test paling berguna untuk alur kritis aplikasi: login → masuk dashboard, checkout → pembayaran, register → verifikasi. Alur yang kalau rusak = user kabur. 😅


Setup Package integration_test

Package ini bawaan Flutter SDK, tapi harus didaftarkan di pubspec.yaml:

# pubspec.yaml
dev_dependencies:
  integration_test:
    sdk: flutter
  flutter_test:
    sdk: flutter

Jalankan flutter pub get, lalu buat folder integration_test/. Bedanya dengan folder test/: integration test butuh device, jadi nggak jalan dengan flutter test biasa.


Contoh 1: Test Alur Login Lengkap

Kita mulai dari yang paling umum — uji alur login dari layar awal sampai masuk ke halaman utama:

// integration_test/login_flow_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:myapp/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('alur login lengkap: login -> dashboard', (WidgetTester tester) async {
    // Jalankan aplikasi sungguhan (bukan widget terisolasi)
    app.main();
    await tester.pumpAndSettle();

    // 1. Ketik email & password di halaman login
    await tester.enterText(
      find.byKey(const Key('email-field')),
      'budi@mail.com',
    );
    await tester.enterText(
      find.byKey(const Key('password-field')),
      'rahasia123',
    );

    // 2. Tap tombol login
    await tester.tap(find.byKey(const Key('login-button')));
    await tester.pumpAndSettle();

    // 3. Verifikasi: kita pindah ke dashboard
    expect(find.text('Selamat datang, Budi!'), findsOneWidget);
    expect(find.byType(NavigationBar), findsOneWidget);

    // 4. Lanjut: tap menu profil
    await tester.tap(find.byKey(const Key('nav-profile')));
    await tester.pumpAndSettle();
    expect(find.text('Halaman Profil'), findsOneWidget);
  });
}

Perhatikan perbedaannya dengan widget test (#36): di sini kita panggil app.main() — aplikasi berjalan lengkap dengan routing, splash screen, dan koneksi API aslinya. Kalau API-nya butuh data test, siapkan di environment staging.


Contoh 2: Ambil Screenshot + Test Performa

Salah satu keunggulan integration test: bisa ambil screenshot otomatis dan ukur performa (frame rate, waktu render). Ini sangat berguna untuk regression test visual dan verifikasi animasi nggak lag:

// integration_test/product_detail_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:myapp/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('buka detail produk dan catat performa', (WidgetTester tester) async {
    app.main();
    await tester.pumpAndSettle();

    // Tap produk pertama di list
    await tester.tap(find.byKey(const Key('product-0')));
    await tester.pumpAndSettle();

    // Verifikasi halaman detail tampil
    expect(find.byKey(const Key('product-title')), findsOneWidget);
    expect(find.text('Laptop Pro 14"'), findsOneWidget);

    // Simpan screenshot sebagai bukti visual
    await binding.takeScreenshot('product-detail');

    // Ukur performa selama scroll
    final watch = Stopwatch()..start();
    for (var i = 0; i < 5; i++) {
      await tester.drag(find.byType(ListView), const Offset(0, -300));
      await tester.pump();
    }
    watch.stop();

    // Verifikasi rata-rata frame masih mulus
    final summary = await binding.traceAction(() async {
      await tester.drag(find.byType(ListView), const Offset(0, -300));
      await tester.pump();
    });
    debugPrint('Waktu scroll 5x: ${watch.elapsedMilliseconds} ms');
    expect(summary['average_frame_build_time_millis'], lessThan(10));
  });
}

traceAction() mengumpulkan data frame — kalau average_frame_build_time_millis melewati batas, kamu langsung tahu ada jank sebelum user yang mengeluh. 🔥


Menjalankan Integration Test

Karena butuh device, jalankan dengan device aktif (emulator, atau HP via USB):

# Android emulator / device
flutter test integration_test/login_flow_test.dart -d emulator-5554

# Semua test di folder integration_test
flutter test integration_test -d emulator-5554

# iOS simulator
flutter test integration_test -d iphone

Output sukses:

00:42 +1: alur login lengkap: login -> dashboard
00:42 +2: buka detail produk dan catat performa

00:42 +2: All tests passed! 🎉

Tips: Untuk CI/CD (nanti kita bahas di #41), bisa pakai Firebase Test Lab atau device farm supaya nggak perlu nyimpen emulator di server.


Best Practice Integration Testing

1. Fokus ke alur kritis saja — Integration test itu lambat (menit), jadi jangan dipakai untuk semua hal. Pilih 3-5 alur utama: login, checkout, register, pencarian.

2. Test di environment terpisah — Jangan pernah jalankan integration test ke production API. Pakai staging server atau mock server dengan data palsu yang bisa di-reset.

3. Siapkan data awal (seed) — Integration test butuh kondisi yang bisa diprediksi. Buat helper yang mengosongkan database dan menyuntik data awal sebelum test jalan.

4. Jangan test hal yang sudah di-cover — Kalau widget test sudah verifikasi form login validasi error, integration test cukup fokus ke alurnya: koneksi API, navigasi, state global.

5. Tambahkan ke CI, bukan cuma lokal — Integration test yang hanya jalan di laptopmu = nggak ada gunanya buat tim. Jalankan otomatis tiap push (kita bahas GitHub Actions di #41).


Piramida Testing Flutter

        /\          Integration Test  — sedikit, lambat, end-to-end
       /  \         (alur kritis: login, checkout)
      /    \
     /──────\       Widget Test       — sedang, cepat, verifikasi UI
    /────────\      (tiap screen & interaksi)
   /──────────\
  /────────────\    Unit Test         — banyak, super cepat, logika
 /──────────────\   (fungsi, model, repository)

Semakin tinggi piramida, semakin sedikit jumlah test dan semakin mahal (lambat) menjalankannya. Semakin rendah, semakin banyak dan murah. Porsi terbesar harus unit test — murah dan cepat mendeteksi bug logika.


Ringkasan

Konsep Penjelasan
Package integration_test (bawaan Flutter SDK)
Environment Emulator / device nyata
Fungsi testWidgets() dengan app.main()
Binding IntegrationTestWidgetsFlutterBinding.ensureInitialized()
Fitur unik Screenshot otomatis, traceAction() untuk performa
Jalankan flutter test integration_test -d <device>

Dengan unit test (#35), widget test (#36), dan integration test (#37), aplikasimu sekarang punya jaring pengaman tiga lapis — mulai dari logika paling dalam sampai perjalanan user paling panjang. Ini modal besar sebelum kita masuk ke Firebase (#38-40) dan CI/CD (#41). 💪


Coba sendiri! Tulis integration test untuk alur paling penting di aplikasimu — login, checkout, atau register — dan jalankan di emulator. Share ke sosial media dan tag @ahsai001 — buktikan aplikasimu layak disebut production-grade! 🚀