Sparround

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 tree
  • find.text(...), find.byType(...), find.byKey(...) locate elements
  • tester.tap(...), tester.enterText(...) interact
  • expect(finder, findsOneWidget) asserts

The `pump` versus `pumpAndSettle` distinction is the most commonly botched part of widget testing:

  • pump() advances one frame
  • pump(Duration) advances by the given time
  • pumpAndSettle() 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