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(),
);
}
}
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.