'How can I test that Flutter BlocListener navigates and renders an initial Welcome page
I am trying to test that a WelcomePage widget is found when my App is first run and no authentication has yet happened.
No matter what I try, I cannot verify that the widget gets rendered.
The test fails every time. I am using expect(find.byType(WelcomePage), findsOneWidget);
Error:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _WidgetTypeFinder:<zero widgets with type "WelcomePage" (ignoring offstage widgets)>
Which: means none were found but one was expected
Test Code:
class MockAuthenticationBloc
extends MockBloc<AuthenticationEvent, AuthenticationState>
implements AuthenticationBloc {}
void main() {
group('App', () {
late AuthenticationBloc authenticationBloc;
setUp(() {
authenticationBloc = MockAuthenticationBloc();
});
group('Unauthenticated', () {
testWidgets('displays the WelcomeScreen', (tester) async {
when(() => authenticationBloc.state)
.thenReturn(const AuthenticationState.unauthenticated());
await tester.pumpApp(
Scaffold(
body: BlocProvider.value(
value: authenticationBloc,
child: const AppView(),
),
),
);
expect(find.byType(WelcomePage), findsOneWidget);
});
});
});
}
app.dart
class App extends StatelessWidget {
const App({
Key? key,
required this.authenticationRepository,
required this.userRepository,
}) : super(key: key);
final AuthenticationRepository authenticationRepository;
final UserRepository userRepository;
@override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: authenticationRepository,
child: BlocProvider(
create: (_) => AuthenticationBloc(
authenticationRepository: authenticationRepository,
userRepository: userRepository,
),
child: const AppView(),
),
);
}
}
class AppView extends StatefulWidget {
const AppView({Key? key}) : super(key: key);
@override
// ignore: library_private_types_in_public_api
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get _navigator => _navigatorKey.currentState!;
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
theme: ThemeData(
appBarTheme:
const AppBarTheme(color: Color.fromARGB(255, 214, 39, 185)),
colorScheme: ColorScheme.fromSwatch(
accentColor: const Color.fromARGB(255, 241, 136, 255),
),
),
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
builder: (context, child) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
_navigator.pushAndRemoveUntil<void>(
HomePage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unauthenticated:
_navigator.pushAndRemoveUntil<void>(
WelcomePage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unknown:
break;
}
},
child: child,
);
},
onGenerateRoute: (_) => SplashPage.route(),
);
}
}
Solution 1:[1]
I think you have to wait for your widget to build for a few frames and settle so add await tester.pumpAndSettle();
:
...
await tester.pumpApp(
Scaffold(
body: BlocProvider.value(
value: authenticationBloc,
child: const AppView(),
),
),
);
// HERE
await tester.pumpAndSettle();
// HERE
expect(find.byType(WelcomePage), findsOneWidget);
});
});
});
}
That way you let flutter build the necessary frames for the blocListener to execute and display the appropiate page.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | croxx5f |