Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test widget using the BLoC pattern

I have been learning Flutter/Dart and the BLoC Pattern. I used this article as my starting point: https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/

I have the bloc class and widget working, but I can't figure out how to test the widget. I'm using a BlocProvider as described in the article, but I can't figure out how to provide the widget with a mocked bloc class.

If I have code like this:

@override
Widget build(BuildContext context) {
  final ProfileBloc profileBloc = BlocProvider.of<ProfileBloc>(context);

  return Scaffold(
      body: Container(
        child: StreamBuilder<AuthModel>(
          stream: profileBloc.outAuthModel,
          initialData: null,
          builder: (BuildContext context, AsyncSnapshot<AuthModel> snapshot) {
            if (snapshot.hasData) {
              return buildProfilePage(context, snapshot.data.profile);
            }
            return buildStartPage();
          },
        ),
      ));
}

I want to mock my ProfileBloc, but it is created in my build() function and requires context. How can I test this widget? I think I need a way to pass in a mocked ProfileBloc, but I can not figure out how to do it. I want to ensure that the widget behaves as intended.

like image 594
jj. Avatar asked Nov 06 '22 23:11

jj.


1 Answers

I had the exact same problem when testing a widget and was able to solve it. Here's the "Before Code" that didn't work and "After Code" that did the trick...

BEFORE CODE

Notice that when pumping the widget MaterialApp is set as the top most widget.

Future<Null> _buildRideCard(WidgetTester tester) async {
      await tester.pumpWidget(MaterialApp( // top most widget 
        localizationsDelegates: [
          AppLocalizationsDelegate(),
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate
        ],
        //some other stuff, irrelevant for this example
        
      ));
    }

AFTER CODE

Notice how MaterialApp widget is now wrapped with BlocProvider and it's blocProviders property is given a list of Blocs that the widget test needs. This fixed my problem and now I don't have any context issues with the bloc in my widget test. Hope it helps ;)

Future<Null> _buildRideCard(WidgetTester tester) async {
      await tester.pumpWidget(BlocProviderTree( // <- magic #1
        blocProviders: [ <- magic #2
          BlocProvider<RideDetailsBloc>(
              bloc: RideDetailsBloc(_setupRidesRepo()))
        ],
        child: MaterialApp(
          localizationsDelegates: [
            AppLocalizationsDelegate(),
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate
          ],
          //some other stuff, irrelevant for this example
        ),
      ));
    }
like image 176
k00na Avatar answered Nov 12 '22 19:11

k00na