'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