Widget testing
The Flutter testing pyramid has three levels: unit (logic, no Flutter), widget (one widget or screen, no device), integration (the whole app on a real device or emulator).
Widget tests are the best balance: they exercise real UI without a device and run in seconds. testWidgets hands you a WidgetTester:
tester.pumpWidget(...)builds the widget treefind.text(...),find.byType(...),find.byKey(...)locate elementstester.tap(...),tester.enterText(...)interactexpect(finder, findsOneWidget)asserts
The `pump` versus `pumpAndSettle` distinction is the most commonly botched part of widget testing:
pump()advances one framepump(Duration)advances by the given timepumpAndSettle()pumps frames until no animation remains
pumpAndSettle is convenient, but with an endless animation — a perpetually spinning CircularProgressIndicator — the test hangs until it times out. Those cases need an explicit pump(Duration(...)).
One more thing: a widget test should make no network requests — substitute the repository with a fake, or the test becomes slow and flaky.
Interview tip. More is expected on Flutter testing from a candidate with a QA background. Show what you assert on: behaviour, not implementation. A check like find.byType(Padding) breaks under refactoring and proves nothing; find.text('Order created') verifies what the user actually sees. It is the same principle as choosing locators in Playwright.
📚 Sources and documentation
- Testing Flutter appsofficialdocs.flutter.dev
The official comparison of when unit, widget and integration tests each fit.
- Introduction to widget testingofficialdocs.flutter.dev
- Finding widgetsofficialdocs.flutter.dev
- WidgetTester classofficialapi.flutter.dev