Test widget using the BLoC pattern

2,547

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

Share:
2,547
jj.
Author by

jj.

Software Developer, Technology fan. https://www.jjerome.com

Updated on December 07, 2022

Comments

  • jj.
    jj. over 1 year

    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.

  • jj.
    jj. over 5 years
    The article mentions sometimes needing to create the bloc locally, and using a StatefulWidget to leverage dispose. So I'll have to use a stateful widget anytime I want to test my widget? I guess I thought I'd be using this provider more often than a local object.
  • Conner
    Conner over 5 years
    A provider isn't the way to go when doing an individual widget test, but it would probably be used in an integration test
  • David
    David about 5 years
    I dont' get it. I have the same problem, but what @jj. posted there is the code from the actual widget, not the test. How do I provide a mocked bloc (ProfileBloc in this case) in my test? The widget will call BlocProvider.of() and I have no way to intercept that AFAIK.