Entegrasyon Testi ve Flutter Geliştirici Araçları: Gerçek Cihazda Uçtan Uca
Entegrasyon testleri Flutter testinin en gerçekçi seviyesidir: fiziksel cihaz veya emülatör, uygulamayı gerçek bir kullanıcının yapacağı gibi çalıştırın ve doğrulayın Kullanıcı arayüzü girişinden arka uç yanıtına kadar tam akış. Test widget'larının aksine Tek bir bileşeni izole eden entegrasyon testleri yığının tamamını kapsar uygulama — navigasyon, durum yönetimi, HTTP, yerel depolama.
Bu makalede eksiksiz bir entegrasyon testi hattı oluşturuyoruz: testleri yazıyoruz
paket ile integration_testbunları emülatörde yerel olarak çalıştırıyoruz,
bunları GitHub Eylemlerinde otomatikleştiriyoruz ve sonunda gerçek fiziksel cihazlarda çalıştırıyoruz
Firebase Test Laboratuvarı ile. Bellek ve CPU'yu analiz etmek için Flutter DevTools'u kullanıyoruz.
yürütme, dağıtımdan önce performans gerilemelerini belirleme.
Ne Öğreneceksiniz
- Flutter'da birim testleri, widget testleri ve entegrasyon testleri arasındaki fark
- Paket kurulumu
integration_testve dosya yapısını test edin - E2E testlerini yazma
IntegrationTestWidgetsFlutterBinding - Kullanımı
find,tap,enterTextepumpAndSettle - Flutter DevTools: Bellek Profilcisi, CPU Profilcisi ve Ağ sekmesi
- GitHub Eylemleri: Emülatördeki entegrasyon testleri için CI/CD hattı
- Firebase Test Lab: Bir dizi fiziksel cihazda çalışıyor
- E2E testinde arka ucu izole etmeye yönelik alaycı stratejiler
Flutter'daki Test Piramidi
Entegrasyon testleri yazmadan önce bunların nereye uygun olduğunu anlamak önemlidir. Çarpıntı test piramidi ve bunların maliyet/kazançlarının diğer düzeylerle karşılaştırılması.
| Tip | Hız | Bağlılık | Bakım | Bunları ne zaman kullanmalı? |
|---|---|---|---|---|
| Birim Testleri | ~1ms test başına | Düşük (izole mantık) | Asgari | İş mantığı, depo, yardımcı programlar |
| Widget Testleri | ~50ms test başına | Orta (Cihazsız kullanıcı arayüzü) | Ortalama | Kullanıcı arayüzü bileşenleri, etkileşimler |
| Entegrasyon Testi | ~Test başına 30s | Yüksek (gerçek cihaz) | Yüksek | Kritik akışlar, E2E regresyonu |
Temel kural: %70 birim testleri, %20 widget testleri, %10 entegrasyon testleri. testleri entegrasyon değerli ama pahalıdır; bunları kritik iş akışları için kullanın (oturum açma, ödeme, katılım) bir gerilemenin gerçek zarara neden olduğu durumlarda.
Proje Kurulumu
# pubspec.yaml: dipendenze per integration testing
dependencies:
flutter:
sdk: flutter
integration_test:
sdk: flutter # gia incluso nell'SDK Flutter
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
mocktail: ^1.0.4 # per mock degli HTTP client
fake_async: ^1.3.1 # per controllare il tempo nei test
# Struttura directory raccomandata
# test/ unit test e widget test
# integration_test/ integration test
# app_test.dart
# flows/
# auth_flow_test.dart
# checkout_flow_test.dart
# helpers/
# test_helpers.dart
İlk Entegrasyon Testi
Entegrasyon testlerinin bağlanması, widget testlerininkinden farklıdır:
IntegrationTestWidgetsFlutterBinding.ensureInitialized() başlatmak
Yerel cihazla iletişim kuran ve performans ölçümlerinin toplanmasını sağlayan bağlama.
// integration_test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
// OBBLIGATORIO: inizializza il binding integration test
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('App Integration Tests', () {
testWidgets('App si avvia e mostra la home page', (tester) async {
// Avvia l'app completa (non un widget isolato)
app.main();
await tester.pumpAndSettle(); // Aspetta che tutte le animazioni finiscano
// Verifica che la home page sia visibile
expect(find.text('Benvenuto'), findsOneWidget);
expect(find.byKey(const Key('home_page')), findsOneWidget);
});
testWidgets('Navigazione tra le tab funziona', (tester) async {
app.main();
await tester.pumpAndSettle();
// Tap sulla tab Profile
await tester.tap(find.byKey(const Key('nav_profile')));
await tester.pumpAndSettle();
// Verifica che la pagina profilo sia caricata
expect(find.byKey(const Key('profile_page')), findsOneWidget);
// Torna alla Home
await tester.tap(find.byKey(const Key('nav_home')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('home_page')), findsOneWidget);
});
});
}
Tam Kimlik Doğrulama Akışını Test Etme
Oturum açma akışı, entegrasyon testi için ideal adaydır: Kullanıcı arayüzü, doğrulama, HTTP çağrısı, belirteç depolama ve oturum açma sonrası gezinme.
// integration_test/flows/auth_flow_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Auth Flow', () {
testWidgets('Login con credenziali valide naviga alla home', (tester) async {
app.main();
await tester.pumpAndSettle();
// Trova e compila il campo email
final emailField = find.byKey(const Key('email_field'));
expect(emailField, findsOneWidget);
await tester.tap(emailField);
await tester.enterText(emailField, 'test@example.com');
// Compila il campo password
final passwordField = find.byKey(const Key('password_field'));
await tester.tap(passwordField);
await tester.enterText(passwordField, 'password123');
// Chiudi la tastiera
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
// Tap sul pulsante di login
await tester.tap(find.byKey(const Key('login_button')));
// Aspetta che il login HTTP completi (max 5 secondi)
await tester.pumpAndSettle(const Duration(seconds: 5));
// Verifica redirect alla home page
expect(find.byKey(const Key('home_page')), findsOneWidget);
expect(find.byKey(const Key('login_page')), findsNothing);
});
testWidgets('Login con credenziali errate mostra errore', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const Key('email_field')),
'wrong@example.com',
);
await tester.enterText(
find.byKey(const Key('password_field')),
'wrongpassword',
);
await tester.tap(find.byKey(const Key('login_button')));
await tester.pumpAndSettle(const Duration(seconds: 5));
// Deve apparire il messaggio di errore
expect(find.text('Credenziali non valide'), findsOneWidget);
// Deve rimanere sulla login page
expect(find.byKey(const Key('login_page')), findsOneWidget);
});
testWidgets('Validazione form: email non valida blocca il submit', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const Key('email_field')),
'email-non-valida',
);
await tester.tap(find.byKey(const Key('login_button')));
await tester.pumpAndSettle();
// Errore di validazione visibile (no HTTP call effettuata)
expect(find.text('Inserisci un\'email valida'), findsOneWidget);
});
});
}
Performans Metriklerinin Toplanması
Entegrasyon testi yalnızca işlevsel doğruluk değildir: bağlama entegrasyon testi Çalışma zamanında kare zamanlaması, bellek ve görüntü oluşturma ölçümlerini toplamanıza olanak tanır testin CI'da ayrıştırılabilecek bir JSON raporu üretilmesi.
// integration_test/performance_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Scrolling della lista prodotti: performance test', (tester) async {
app.main();
await tester.pumpAndSettle();
// Naviga alla lista prodotti
await tester.tap(find.byKey(const Key('nav_products')));
await tester.pumpAndSettle();
// Raccoglie metriche durante il scroll
await binding.watchPerformance(() async {
// Scroll veloce per 5 paginate
for (int i = 0; i < 5; i++) {
await tester.fling(
find.byKey(const Key('products_list')),
const Offset(0, -500),
3000, // velocita pixels/secondo
);
await tester.pumpAndSettle();
}
},
reportKey: 'products_scroll_perf');
// Le metriche vengono salvate automaticamente in un file JSON
// Accessibile via: flutter drive --profile
});
}
// Comando per raccogliere metriche in modalita profile:
// flutter drive \
// --driver=test_driver/integration_test.dart \
// --target=integration_test/performance_test.dart \
// --profile
Flutter DevTools: Derin Profil Oluşturma
Flutter DevTools, tarayıcı aracılığıyla erişilebilen bir teşhis araçları paketidir uygulama hata ayıklama veya profil modunda çalışırken. Tanımlanacak en kullanışlı sekmeler performans sorunları Performans sekmesi (kare zamanlaması), the Bellek sekmesi (yığın tahsisi) ve Ağ sekmesi (HTTP gecikmesi).
# Avviare DevTools durante un integration test
# 1. Lancia l'app in modalita debug su emulatore
flutter run --debug
# 2. Apri DevTools (automaticamente o manualmente)
flutter pub global run devtools
# 3. Oppure direttamente da VS Code / Android Studio
# View > Command Palette > Flutter: Open DevTools
# Performance tab: comandi utili
# - "Record" per catturare una sessione
# - "Enhance Tracing" per shader e build details
# - Filtra per "Janky frames" (rosso = sopra 16ms budget)
# Memory tab: identificare memory leak
# - "GC" button: forza garbage collection
# - "Snapshot" prima e dopo un'operazione
# - Confronta gli heap dump per trovare oggetti non rilasciati
# Network tab
# - Mostra tutte le richieste HTTP/HTTPS
# - Timing breakdown: DNS, connect, send, wait, receive
# - Filtro per URI pattern
Flutter'da Yaygın Bellek Sızıntısı Deseni
Entegrasyon testlerinde (ve üretimde) en yaygın bellek sızıntısıAnimasyon Kontrolörü
istekli değil: İçinde oluşturulan bir denetleyici initState karşılık gelen olmadan
dispose() dinleyicileri biriktirir ve asla çöp toplayıcı tarafından serbest bırakılmaz.
Flutter DevTools Bellek sekmesi, onu kök yolunu koruyan bir nesne olarak tanımlar.
GitHub Eylemleri: Entegrasyon Testi için CI/CD İşlem Hattı
CI'da entegrasyon testlerini çalıştırmak için bir Android emülatörü veya iOS simülatörü gerekir. Burada hızlı derleme süreleri için optimize edilmiş eksiksiz bir GitHub Eylemleri kurulumu bulunmaktadır.
# .github/workflows/integration-tests.yml
name: Integration Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
integration-tests-android:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Java (richiesto per emulatore Android)
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.0'
channel: 'stable'
cache: true # cache delle dipendenze Flutter
- name: Install dependencies
run: flutter pub get
- name: Enable KVM (accelerazione hardware emulatore)
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Avvia emulatore Android
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 33
arch: x86_64
profile: Nexus 6
avd-name: integration_test_avd
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim
disable-animations: true # disabilita animazioni per test piu veloci
script: |
flutter test integration_test/ \
--flavor development \
-d emulator-5554 \
--dart-define=ENVIRONMENT=test
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-results
path: build/integration_test_results/
Firebase Test Laboratuvarı: Fiziksel Cihaz Matrisi
Emülatörlerle GitHub Eylemleri temel testleri kapsar ancak fiziksel cihazlar gerçek donanım farklılıklarına (GPU, sensörler, OEM Android çeşitleri) sahipler emülatör oynatılmıyor. Firebase Test Lab bir cihaz filosu sunuyor uygulamanın çalıştırılacağı fiziksel dosyalar.
# Preparazione dell'APK per Firebase Test Lab
# 1. Build dell'app e del test APK separati
flutter build apk --debug --target-platform android-arm64
flutter build apk --debug \
--target=integration_test/app_test.dart \
--target-platform android-arm64
# 2. Upload e avvio del test su Firebase Test Lab via gcloud CLI
gcloud firebase test android run \
--type instrumentation \
--app build/app/outputs/apk/debug/app-debug.apk \
--test build/app/outputs/apk/debug/app-debug-androidTest.apk \
--device model=Pixel6,version=33,locale=it,orientation=portrait \
--device model=SamsungS22,version=32,locale=it,orientation=portrait \
--device model=OnePlus9,version=31,locale=it,orientation=portrait \
--timeout 5m \
--results-bucket=gs://my-project-test-results \
--results-dir=integration_tests/$(date +%Y%m%d_%H%M%S)
# Integrazione Firebase Test Lab in GitHub Actions
- name: Authenticate Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Setup gcloud CLI
uses: google-github-actions/setup-gcloud@v2
- name: Build test APKs
run: |
flutter build apk --debug
flutter build apk --debug \
--target=integration_test/app_test.dart
- name: Run tests on Firebase Test Lab
run: |
gcloud firebase test android run \
--type instrumentation \
--app build/app/outputs/apk/debug/app-debug.apk \
--test build/app/outputs/apk/debug/app-debug-androidTest.apk \
--device model=Pixel6,version=33,locale=it,orientation=portrait \
--timeout 5m \
--results-bucket=gs://my-app-test-results
Güvenilir E2E Testleri için Alaycı Strateji
Gerçek bir arka uca dayanan testler hassastır: sunucu kapalı olabilir,
veriler değişebilir, gecikme değişir. Test için en iyi strateji
entegrasyon ve kullanım sahte yerel sunucu (gibi mockito
veya sahte bir HTTP sunucusu) aracılığıyla yapılandırılabilir --dart-define.
# main.dart: configurazione per ambiente test
// main.dart
void main() {
// Legge la variabile di ambiente iniettata dalla CI
const environment = String.fromEnvironment(
'ENVIRONMENT',
defaultValue: 'production',
);
if (environment == 'test') {
// Usa il mock HTTP client per i test di integrazione
HttpOverrides.global = _MockHttpOverrides();
}
runApp(
ProviderScope(
overrides: environment == 'test'
? [
// Override Riverpod provider con il mock repository
apiClientProvider.overrideWithValue(MockApiClient()),
]
: [],
child: const MyApp(),
),
);
}
class _MockHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
// Intercetta tutte le richieste HTTP e restituisce dati fissi
return MockHttpClient();
}
}
Kararlı Entegrasyon Testleri için En İyi Uygulamalar
-
Açık Anahtarları Kullanın: her etkileşimli widget'ın bir
Keytestlerde istikrarlı bir şekilde bulunabilmesini sağlamak için sabit. -
Sen tercih edersin
pumpAndSettleapump:pumpAndSettleasılı tüm animasyonları ve çerçeveleri bekleyin Böylece zamanlamadan kaynaklanan hatalı testler azaltılır. -
Açık zaman aşımları: Amerika
pumpAndSettle(Duration(seconds: 5))varsayılan sonsuz zaman aşımı yerine eşzamansız işlemler için. -
Testler arasında durumun sıfırlanması: Amerika
tearDowniçin SharedPreferences'ı, yerel veritabanlarını ve kimlik doğrulama belirteçlerini temizleyin. -
Animasyonları devre dışı bırakın: ABD CI boru hatlarında
--no-enable-impellerve hızlandırmak için animasyonları devre dışı bırakın %40 oranında icra.
Sonuçlar
Entegrasyon testleri, üretime geçmeden önceki son savunma hattıdır: etkileşimden ortaya çıktıkları için birim testlerden kaçan regresyonları yakalarlar gerçek donanımdaki gerçek bileşenler arasında. Maliyet yüksektir – test başına 30-60 saniye, kurulum karmaşıklığı, sürekli bakım — ancak kritik iş akışları için ROI tartışılmaz.
Her PR ve Firebase hakkında hızlı geri bildirim için GitHub Eylemlerinin birleşimi Heterojen fiziksel cihazların kapsamına yönelik Test Laboratuvarı bir güvenlik ağı oluşturur güvenle konuşlandırmanıza olanak tanıyan sağlam bir yapıya sahiptir. Flutter DevTools resmi tamamlıyor çalışma zamanında performansın görünürlüğünü sağlamak, testleri dönüştürmek Basit doğruluk kontrollerinden izleme araçlarına kadar entegrasyon kullanıcı deneyiminin kalitesi.







