Write a flow test
Flow tests (the gadfly harness in packages/flow_test) drive real screens
end-to-end and assert both the rendered UI and the analytics transcript, across
light / dark / focus trips. They are the backbone of the Test Gallery.
What gets mocked
The canonical harness mocks repositories — the presentation seam blocs depend
on — via MocksContainer + testAppBuilder. (A real-stack variant fakes the
adapter instead — an in-memory client with real services — where end-to-end
SDK coverage is worth it. Unit tests use MockClient from client_sdk_testing.)
See app/test/README.md.
Anatomy
flowTest<MocksContainer>(
'sign in → home',
config: createFlowConfig(),
descriptions: [ /* EPIC / STORY / AC for the gallery tree */ ],
test: (tester) async {
await tester.setUp(arrangeBeforePumpApp: (arrange) async {
when(() => arrange.mocks.authRepository.signIn(...)).thenAnswer((_) async {});
});
await tester.screenshot(
description: 'the sign-in page',
actions: (a) async { await a.userAction.tap(find.byKey(SignInPage.submitKey)); },
expectations: (e) { e.expect(find.byType(HomePage), findsOneWidget); },
expectedEvents: ['[ANALYTIC] [page]: HomeRoute'],
);
},
);
expectedEventsasserts the analytics/page transcript — the funnel events (e.g.today_section_shown,bounty_claimed) are proven here.- Maestro note: Maestro resolves
id:againstSemantics(identifier: '…'), not FlutterKeys. Put aSemantics(identifier:)on every control Maestro taps; keepKeys forfind.byKeyin tests.
Run + regenerate goldens
cd app
fvm flutter test test/flows/<feature>_test.dart # logic + assertions
# regenerate the golden screenshots (also feeds the Test Gallery):
fvm flutter test test/flows/<feature>_test.dart \
--update-goldens --dart-define=createScreenshots=true
Tip: when checking a whole-suite run, capture flutter's real exit code (
fvm flutter test > /tmp/t.txt 2>&1; echo "EXIT=$?") — a piped| tailreports the pipe's exit, not the test result, and can hide failures (and OOM hangs).