Mobilni razvoj
FlutterTestiranjeCI/CDRazvoj mobilnih aplikacijaQAAutomatizacija

Strategija testiranja u Flutteru: unit, widget, integracijski i golden testovi za brz i pouzdan CI (2026)

AO
Adrijan Omićević
·16 min čitanja

# Što ćete naučiti#

Strategija testiranja u Flutteru nije stvar maksimiziranja broja testova. Radi se o maksimiziranju povjerenja po minuti CI vremena i minimiziranju flaky padova koji usporavaju izdanja.

Ovaj vodič definira praktičnu Flutter piramidu testiranja, pokazuje kada koristiti unit, widget, integracijske i golden testove te objašnjava kako strukturirati testove za codebaseove po feature-first pristupu i clean architecture. Uključuje i konkretne CI savjete kako zadržati buildove brzim i pouzdanim, s primjerima mockanja, fakeova, golden testiranja i smanjenja flakinessa.

Ovo možete upariti s našim širim QA pristupom u članku QA strategija testiranja za web i mobile te detaljima CI postavki u Flutter CI/CD s GitHub Actions, Codemagic i Fastlane.

# Definirajte Flutter piramidu testiranja#

Piramida testiranja govori o cijeni testova. Unit testovi su najjeftiniji i najdeterminističkiji, dok su end-to-end testovi skupi i najskloniji flakinessu zbog stvarnih uređaja, timinga i platformskih ovisnosti.

Dobra Flutter strategija testiranja obično izgleda ovako:

SlojTipičan udioŠto pokrivaPokreće se u CI-uGlavni rizici
Unit testovi60 do 80 postoČista poslovna logika, validacija, mapperi, reduceri, use caseoviSvaki PRPretjerano mockanje, testiranje implementacije umjesto ponašanja
Widget testovi15 do 30 postoPonašanje UI-ja, prijelazi stanja, routing, osnove a11ySvaki PRFlakiness zbog animacija, timera, async rebuildova
Golden testovi5 do 15 postoVizualne regresije za komponente i stabilne ekraneSvaki PR ili nightlyRazlike u fontovima i renderiranju po platformama
Integracijski testovi1 do 5 postoKritične korisničke putanje preko pluginova i platformeNightly i release graneSpori, flaky, varijabilnost uređaja i okoline

🎯 Ključni zaključak: Integracijske testove koristite za zaštitu tokova ključnih za prihod, ne za provjeru svakog UI stanja. Većina ispravnosti treba dolaziti iz unit i widget testova.

Mapiranje piramide na stvarne rizike u aplikaciji#

Koristite piramidu da pokrijete četiri kategorije regresija:

  1. 1
    Regresije poslovnih pravila: pravila cijena, validacija, feature gating, logika offline queuea. To pripada unit testovima.
  2. 2
    Regresije ponašanja UI-ja: loading stanja, error stanja, navigacija, interakcije s formama. To pripada widget testovima.
  3. 3
    Vizualne regresije: promjene paddinga, tipografije, krive boje, razbijeni layouti. To pripada golden testovima.
  4. 4
    Regresije platforme i pluginova: push notifikacije, deep linkovi, kamera, file picker, keychain, tokovi payment SDK-a. To pripada integracijskim testovima.

Ciljevi piramide koje možete stvarno mjeriti#

Umjesto lova na postotak coveragea, pratite metrike pipelinea:

MetrikaDobar ciljZašto je bitno
Trajanje PR CI-amanje od 10 minutaDrži review loop kratkim, smanjuje prebacivanje konteksta
Stopa flaky padovamanje od 1 posto pokretanjaFlaky testovi su gori od nikakvih jer ruše povjerenje
Prosječno vrijeme do dijagnoze padamanje od 10 minutaPad treba ukazivati na konkretan uzrok, ne slučajni timeout
Trajanje integracijskog suitea10 do 25 minutaAko traje satima, tim ga prestaje pokretati lokalno

Ako trebate sloj governancea i proces oko ovoga, uskladite ga s našim QA strategy playbookom.

# Struktura projekta: Feature-first ili clean architecture testiranje#

Struktura testova treba odražavati strukturu produkcijskog koda. U suprotnom, testovi postaju “negdje drugdje” i s vremenom propadaju.

Ako već koristite clean architecture ili feature-first, pratite iste granice i u testovima. Ako niste sigurni koji smjer odabrati, pogledajte Flutter arhitektura aplikacije: clean architecture vs feature-first.

Feature-first raspored test foldera#

Praktičan raspored koji se dobro skalira:

PutanjaŠto ide tamoNapomene
test/features/checkout/domain/testovi use caseova, entitetaČisti Dart, bez Flutter ovisnosti
test/features/checkout/data/testovi repozitorija, mappera, DTO-ovaKoristite fakeove za local storage i HTTP
test/features/checkout/presentation/widget testovi za ekrane i widgeteKoristite pumpWidget s mockanim providerima
test/shared/zajednički fakeovi, fixturei, matcheriDržite test utilse lako pronađivima
test/goldens/golden testovi i baselineoviOdvojite radi finog podešavanja CI koraka

Clean architecture raspored test foldera#

Ako dijelite po slojevima kroz cijelu aplikaciju:

PutanjaŠto ide tamoNapomene
test/domain/entiteti, value objecti, use caseoviVisok ROI za unit testove
test/data/API klijenti, repozitoriji, persistence adapteriKoristite fakeove za IO i determinističke satove
test/presentation/widget testovi, testovi state managementaPreferirajte testiranje ponašanja, ne privatnog stanja
integration_test/integracijski testoviDržite minimalno i stabilno
test/goldens/golden testoviPokrećite na jednom OS-u u CI-u da izbjegnete diffs

ℹ️ Napomena: Izbjegavajte test/ strukturu foldera koja nema veze s folderima aplikacije. Kad developer mijenja feature, treba odmah znati gdje su testovi.

# Unit testovi: Brzo povjerenje za poslovnu logiku#

Unit testovi su mjesto gdje dobivate brzinu i determinističnost. Cilj je izolirati čistu logiku od frameworka i IO-a.

Što unit testirati u Flutter aplikacijama#

Ciljevi s visokim ROI-jem:

CiljPrimjerZašto se isplati
Validacijapravila za email, lozinku, VAT IDČesto puca, lako testirati
Cijene i zbrojevipopusti, kuponi, poreziDirektan utjecaj na biznis
MapperiDTO u domain konverzijeSprječava tihu korupciju podataka
Use caseoviPlaceOrder, RefreshSessionŠtiti core tokove bez UI kompleksnosti
Reduceri i prijelazi stanjapaginacija, filtriranjeBugove je teško uočiti kroz UI

Mocking vs fakeovi: kada koristiti što#

Čest način kako testovi propadnu je pretjerano mockanje. Mockanje svega čini testove krhkima jer tvrde “kako”, umjesto “što”.

Koristite ovo pravilo:

AlatKoristite kadaPrimjer
MockTrebate provjeriti pozive i parametreProvjerite da repozitorij zove trackPurchase(amount)
FakeTrebate ponašanje sa stanjemIn-memory baza, fake cache s evictionom
StubTrebate samo fiksnu povratnu vrijednostclock.now() vraća fiksno vrijeme

⚠️ Upozorenje: Pretjerano mockanje vodi do lažnog povjerenja. Ako mockate obje strane granice, testovi mogu prolaziti dok je stvarna integracija pokvarena.

Primjer: unit test s fake repozitorijem#

Ovaj primjer izbjegava mrežu i izbjegava krhka očekivanja poziva.

Dart
// dart
class FakeCartRepository implements CartRepository {
  final List<CartItem> _items = [];
 
  @override
  Future<List<CartItem>> getItems() async => List.unmodifiable(_items);
 
  @override
  Future<void> addItem(CartItem item) async => _items.add(item);
}
 
void main() {
  test('AddToCartUseCase adds item to repository', () async {
    final repo = FakeCartRepository();
    final useCase = AddToCartUseCase(repo);
 
    await useCase(CartItem(id: 'sku_1', qty: 2));
 
    final items = await repo.getItems();
    expect(items.single.id, 'sku_1');
    expect(items.single.qty, 2);
  });
}

Ovaj test radi u milisekundama i pada s preciznim razlogom.

Primjer: unit test s mockom za provjeru side effecta#

Kad trebate provjeriti interakciju, koristite mock framework. Držite asercije fokusirane na ponašanje koje je bitno.

Dart
// dart
abstract class Analytics {
  void track(String event, Map<String, Object?> props);
}
 
class CheckoutComplete {
  final Analytics analytics;
  CheckoutComplete(this.analytics);
 
  void call(double amount) {
    analytics.track('checkout_complete', {'amount': amount});
  }
}

Test treba provjeriti naziv eventa i bitne ključeve u payloadu. Izbjegavajte provjeravati svako svojstvo, osim ako je kritično.

# Widget testovi: Provjera ponašanja UI-ja bez uređaja#

Widget testovi su “sweet spot” za Flutter UI logiku. Izvode se brzo i ne trebaju pravi uređaj, ali i dalje validiraju kompoziciju, renderiranje i interakcije.

Što pokriti widget testovima#

Fokusirajte se na ponašanje, ne na pixel-perfect vizuale:

PonašanjePrimjer asercije
Loading i error stanjaprikazuje spinner, zatim error tekst
Validacija inputasubmit je onemogućen dok input nije validan
Odluke navigacijetap na button gura ispravnu rutu
Prijelazi stanjarefresh okida reload i ažurira listu
Osnove pristupačnostitappable elementi imaju label i dohvatljivi su

Widget test harness: držite ga konzistentnim#

Napravite jedan helper koji omata MaterialApp, lokalizaciju, teme i dependency injection. Time smanjujete duplikaciju i rizik flakinessa.

Dart
// dart
Widget testApp(Widget child) {
  return MaterialApp(
    theme: ThemeData.light(),
    home: Scaffold(body: child),
  );
}
 
void main() {
  testWidgets('Login button enabled when form valid', (tester) async {
    await tester.pumpWidget(testApp(const LoginForm()));
 
    await tester.enterText(find.byKey(const Key('email')), 'a@b.com');
    await tester.enterText(find.byKey(const Key('password')), 'password123');
    await tester.pump();
 
    final button = find.byKey(const Key('submit'));
    expect(tester.widget<ElevatedButton>(button).enabled, true);
  });
}

Ako koristite Riverpod, Bloc ili Provider, omotajte testApp helper potrebnim scopeom i tamo injektirajte fakeove.

💡 Savjet: Preferirajte find.byKey radi stabilnosti u widget testovima. Finderi temeljeni na tekstu pucaju kad se copy promijeni, a finder based on type puca kad se widgeti refaktoriraju.

Smanjenje flakinessa u widget testovima#

Većina flaky widget testova dolazi iz async rebuildova i animacija.

Koristite ove taktike:

  1. 1
    Kontrolirajte animacije: izbjegavajte čekati implicitne animacije osim ako ih stvarno testirate.
  2. 2
    Koristite eksplicitni pumping: preferirajte pump() i pump(const Duration(...)) umjesto pumpAndSettle() kad znate što čekate.
  3. 3
    Izbjegavajte stvarne timere i sat: injektirajte clock sučelje ili koristite fiksne timestampove.
  4. 4
    Izbjegavajte pravi HTTP: koristite fakeove ili lokalne fixturee.
  5. 5
    Držite stanje determinističnim: bez random seedova osim ako su fiksirani.

Praktična smjernica: tretirajte pumpAndSettle() kao zadnju opciju. Može “visiti” ako postoji ponavljajuća animacija ili stream.

# Golden testovi: Uhvatite vizualne regresije rano#

Golden testovi su snapshot testovi za UI. Savršeni su za design systeme, ponovno upotrebljive widgete i stabilne ekrane.

Nisu idealni za ekrane koji uključuju real-time podatke, mape, video ili dinamičan sadržaj koji često mijenja layout.

Što golden testirati#

Ciljevi s visokim ROI-jem:

Tip UI-jaPrimjerZašto radi
Komponente design systemabuttoni, inputi, karticeStabilno, reusable, velik blast radius
“Hero” ekranionboarding, pricing, checkoutStakeholderima su vizuali važni
Empty, loading, error stanjaekran prazne liste, offline bannerLako se razbije u refactorima

Osnove golden testova#

Golden test treba kontrolirati:

  • Učitavanje fontova i renderiranje teksta
  • Veličinu uređaja i pixel ratio
  • Locale
  • Temu
  • Ulazne podatke

Kad god je moguće, držite widget malim i hranite ga eksplicitnim fixtureima.

Dart
// dart
testWidgets('ProductCard matches golden', (tester) async {
  await tester.pumpWidget(
    testApp(
      SizedBox(
        width: 360,
        child: ProductCard(
          title: 'Running Shoes',
          price: 79.99,
          imageUrl: null,
        ),
      ),
    ),
  );
 
  await expectLater(
    find.byType(ProductCard),
    matchesGoldenFile('goldens/product_card.png'),
  );
});

Smanjenje golden diffova između računala#

Golden testovi mogu padati zbog razlika u fontovima i renderiranju među OS verzijama. U CI-u odaberite jednu okolinu i standardizirajte je.

Preporučene prakse:

PraksaRezultat
Pokrećite goldene na jednom OS-u u CI-uKonzistentan rendering pipeline
Pinajte Flutter verzijuSprječava promjene renderiranja između stable izdanja
Učitajte i bundlajte test fontoveIzbjegava razlike u sistemskim fontovima
Koristite fiksnu surface sizeStabilan layout
Izbjegavajte dinamične sjene i blur gdje je mogućeSmanjuje suptilne pixel diffove

⚠️ Upozorenje: Ne pokrećite isti golden suite na macOS-u, Linuxu i Windowsu i očekujte identičan output. Odaberite jedno referentno okruženje i tretirajte ga kao source of truth.

# Integracijski testovi: Mali suite, veliko povjerenje#

Integracijski testovi validiraju end-to-end tokove i granice pluginova. Neophodni su za tokove koji se ne mogu dokazati bez pokrenute aplikacije.

Primjeri gdje se integracijski testovi isplate:

  • Deep link otvara ispravan ekran s ispravnim parametrima
  • Login i refresh tokena rade s pravim auth SDK-om
  • Purchase ili subscription flow radi na stagingu
  • Tap na push notifikaciju vodi na ispravan ekran

Držite integracijske testove minimalnima i stabilnima#

Dobar integracijski suite je skup “smoke testova” za kritične putanje.

Koristite kriterije odabira:

PitanjeAko je odgovor da, dodajte integracijsko pokrivanje
Generira li tok prihod ili sprječava churncheckout, subscription, onboarding
Dotiče li tok native pluginovekamera, biometrika, storage, notifikacije
Ovisi li tok o više ekrana i navigacijimulti-step wizardi
Je li kvar skup u produkcijicrash, gubitak podataka, problemi s plaćanjem

Primjer: stabilan pattern integracijskog testa#

Koristite eksplicitna čekanja i robusne findere. Preferirajte keyeve i semantics labele.

Dart
// dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
 
void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
  testWidgets('user can log in and see home', (tester) async {
    await tester.pumpWidget(const App());
 
    await tester.enterText(find.byKey(const Key('email')), 'test@acme.com');
    await tester.enterText(find.byKey(const Key('password')), 'Password123!');
    await tester.tap(find.byKey(const Key('login')));
    await tester.pump(const Duration(seconds: 2));
 
    expect(find.byKey(const Key('home-screen')), findsOneWidget);
  });
}

Ovo je namjerno jednostavno. Za ozbiljne aplikacije trebat će vam i test-only config koji pokazuje na staging backend i koristi seedane test račune.

# Mocking i fakeovi kroz slojeve: praktični obrasci#

Konzistentna granica ovisnosti je ono što testove čini lakima. Ako vaš kod iz UI-ja direktno ide u Dio, SharedPreferences ili platform channels, testovi će biti spori i bolni.

Preporučene točke apstrakcije#

Vanjska ovisnostApstrakcijaZamjena u testu
HTTP klijentApiClient interfacefake klijent koji čita fixturee
Lokalna pohranaKeyValueStore interfacein-memory map fake
VrijemeClock interfacefixed clock stub
UUID i randomIdGenerator interfacedeterministički generator
Platform channelservice interfacefake servis, ili integracijski test

Primjer: fake API klijent iz JSON fixturea#

Ovo drži podatke determinističnima i omogućuje pouzdano testiranje error putanja.

Dart
// dart
class FakeApiClient implements ApiClient {
  final Map<String, dynamic> _responsesByPath;
 
  FakeApiClient(this._responsesByPath);
 
  @override
  Future<Map<String, dynamic>> get(String path) async {
    final data = _responsesByPath[path];
    if (data == null) throw Exception('404 for $path');
    return data as Map<String, dynamic>;
  }
}

Uparite ovo s JSON fixtureima učitanima iz test/fixtures/ i vaši testovi repozitorija postaju brzi i smisleni.

# CI: Učinite testove brzim, determinističnim i pouzdanim#

Flutter strategija testiranja radi samo ako je CI dovoljno brz da ga developeri ne zaobilaze. Najbolji suiteovi su oni koji se pokreću na svakom PR-u.

Za end-to-end postave pipelinea i signing, pogledajte Flutter CI/CD s GitHub Actions, Codemagic i Fastlane.

Podijelite CI u razine koje prate piramidu#

Praktična podjela CI-a:

JobPokreće se naŠto pokrećeCiljano vrijeme
analyze-and-unitsvaki PRflutter analyze, unit testovi2 do 5 minuta
widget-and-goldensvaki PRwidget testovi, goldeni3 do 7 minuta
integration-smokemain grana nightly5 do 20 integracijskih testova10 do 25 minuta
release-validationrelease granepuni integration, smoke, buildovisi o platformi

Naredbe koje ćete stvarno koristiti#

Bash
# bash
flutter --version
flutter pub get
flutter analyze
 
flutter test --coverage
flutter test test/widget
flutter test test/goldens
 
flutter test integration_test -d "iPhone 15 Pro"

Držite ove naredbe usklađene između lokalnih skripti i CI-a kako biste smanjili “radi na mom stroju” drift.

Cacheanje i pinanje za smanjenje CI vremena#

Dobici u brzini dolaze iz uklanjanja ponavljajućeg rada:

OptimizacijaTipičan utjecajNapomene
Cache pub paketaštedi 1 do 3 minuteovisi o veličini ovisnosti
Cache Flutter SDK-aštedi 1 do 5 minutaposebno na svježim runnerima
Pinanje Flutter verzijesmanjuje slučajne padovekoristite version file u repozitoriju
Paralelizacija jobovasmanjuje ukupno wall timeunit i widget jobovi mogu paralelno

💡 Savjet: Golden testove tretirajte kao PR gate tek nakon što stabilizirate fontove i renderiranje u CI-u. Prije toga, pokrećite goldene nightly i prvo riješite determinističnost.

Checklist za smanjenje flakinessa u CI-u#

Većinu flaky Flutter testova uzrokuju vrijeme, async i razlike u okolini.

Koristite ovaj checklist:

  1. 1
    Bez stvarne mreže u unit i widget testovima. Koristite fakeove i fixturee.
  2. 2
    Determinističko vrijeme. Injektirajte clock i izbjegavajte DateTime.now() u testovima formatiranja vremena u UI-ju.
  3. 3
    Stabilni fontovi za goldene. Bundlajte test fontove i držite se jednog CI OS-a.
  4. 4
    Izbjegavajte pumpAndSettle() u dugim tokovima. Zamijenite s eksplicitnim pump trajanjem i uvjetima čekanja.
  5. 5
    Retry samo za integracijske testove. Retry za unit testove skriva stvarnu nedeterminističnost.
  6. 6
    Skupljajte artefakte na pad. Za integraciju, spremite screenshotove i logove kako biste ubrzali debugging.

Ako trebate holistički pristup izvan samog Fluttera, uskladite se s našom QA i automation strategijom.

# Sve zajedno: praktični blueprint za Flutter strategiju testiranja#

Strategija je korisna samo ako postane ponovljiv workflow.

Preporučeni baseline za produkcijsku aplikaciju#

Krenite s ovim baselineom i prilagodite prema riziku:

PodručjeBaselinePrimjer
Domain logika50 do 150 unit testovavalidacija, izračuni, use caseovi
Ponašanje UI-ja30 do 80 widget testovaključni ekrani, error stanja, navigacija
Vizualne regresije10 do 30 goldenakomponente, ključni ekrani
End-to-end tokovi5 do 15 integracijskih testovalogin, checkout, onboarding, deep linkovi

Kako odabrati ispravan tip testa za bug#

Kad se bug pojavi, dodajte test na najnižem sloju koji bi ga uhvatio.

Tip bugaNajbolji tip testaZašto
Pogrešni zbrojeviUnit testnajjeftiniji, najbrži, najprecizniji
Button je krivo enableanWidget testvalidira UI logiku bez uređaja
Padding se slomio u refactoruGolden testhvata pixel-level regresiju
Push notifikacija otvara krivi ekranIntegracijski testzahtijeva plugin boundary

Ovo drži CI brzim i sprječava da integracijski suite postane catch-all.

# Ključni zaključci#

  • Gradite Flutter strategiju testiranja oko piramide: puno unit testova, manje widget testova, mali integracijski suite i ciljane golden testove za vizuale.
  • Preferirajte fakeove i fixturee za stateful ovisnosti, a mockove koristite samo kad morate provjeravati interakcije i parametre.
  • Smanjite flakiness kontroliranjem vremena, mreže, animacija i fontova te izbjegavanjem pumpAndSettle() u dugotrajnim widget i integracijskim tokovima.
  • Strukturirajte testove tako da odražavaju codebase, bilo feature-first ili clean architecture, kako bi testovi ostali lako pronađivi i održivi.
  • Podijelite CI na brze PR gateove za analyze, unit, widget i goldene, a integration smoke testove pokrećite nightly ili na release granama uz artefakte.

# Zaključak#

Pouzdana Flutter strategija testiranja je konkurentska prednost: manje produkcijskih regresija, brža izdanja i CI pipeline kojem developeri stvarno vjeruju. Krenite tako da pojačate unit i widget pokrivenost oko poslovno kritične logike, dodate goldene za stabilne UI komponente i držite integracijske testove malima i fokusiranima na putanje s puno pluginova.

Ako želite da auditiramo vaš trenutni Flutter test suite, smanjimo CI vrijeme i implementiramo stabilnu piramidu prilagođenu vašoj arhitekturi, kontaktirajte Samiodu i pretvorit ćemo vaše testiranje u brz i pouzdan proces izdanja.

FAQ

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Više iz kategorije Mobilni razvoj

Sve

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.

Povezani članci