title: How do I mock a bloc in Flutter, with states being emitted in response to events from a widget under test
body: <p>I'm trying to test a widget that makes use of a bloc.  I'd like to be able to emit states from my mocked bloc in response to events being fired by the widget under test.  I've tried a number of approaches without success.  I'm not sure if I'm making some simple error or if I'm approaching the problem all wrong.</p>
  <p>Here is a simplified project which demonstrates my issue.  (the complete code for this can be found at <a href="https://github.com/andrewdixon1000/flutter_bloc_mocking_issue.git" rel="nofollow noreferrer">https://github.com/andrewdixon1000/flutter_bloc_mocking_issue.git</a>)</p>
  <p><strong>very simple bloc</strong></p>
  <pre><code>import 'dart:async';
  
  import 'package:bloc/bloc.dart';
  import 'package:meta/meta.dart';
  
  class MyBloc extends Bloc&lt;MyEvent, MyState&gt; {
    MyBloc() : super(FirstState());
  
    @override
    Stream&lt;MyState&gt; mapEventToState(
      MyEvent event,
    ) async* {
      if (event is TriggerStateChange) {
        yield SecondState();
      }
    }
  }
  
  @immutable
  abstract class MyEvent {}
  
  class TriggerStateChange extends MyEvent {}
  
  
  @immutable
  abstract class MyState {}
  
  class FirstState extends MyState {}
  
  class SecondState extends MyState {}
  </code></pre>
  <p><strong>My widget under test</strong></p>
  <pre><code>import 'package:flutter/material.dart';
  import 'package:flutter_bloc/flutter_bloc.dart';
  
  import 'bloc/my_bloc.dart';
  import 'injection_container.dart';
  
  class FirstPage extends StatefulWidget {
    const FirstPage({Key? key}) : super(key: key);
  
    @override
    _FirsPageState createState() =&gt; _FirsPageState();
  }
  
  class _FirsPageState extends State&lt;FirstPage&gt; {
  
    @override
    Widget build(BuildContext context) {
      return BlocProvider(
        create: (context) =&gt; serviceLocator&lt;MyBloc&gt;(),
        child: Scaffold(
          appBar: AppBar(title: Text(&quot;Page 1&quot;)),
          body: Container(
            child: BlocConsumer&lt;MyBloc, MyState&gt;(
              listener: (context, state) {
                if (state is SecondState) {
                  Navigator.pushNamed(context, &quot;SECONDPAGE&quot;);
                }
              },
              builder: (context, state) {
                if (state is FirstState) {
                  return Column(
                    children: [
                      Text(&quot;State is FirstState&quot;),
                      ElevatedButton(
                          onPressed: () {
                            BlocProvider.of&lt;MyBloc&gt;(context).add(TriggerStateChange());
                          },
                          child: Text(&quot;Change state&quot;)),
                    ],
                  );
                } else {
                  return Text(&quot;some other state&quot;);
                }
              },
            ),
          ),
        ),
      );
    }
  }
  </code></pre>
  <p><strong>my widget test</strong>
  This is where I'm struggling.  What I'm doing is loading the widget and then tapping the button.  This causes the widget to add an event to the bloc.  What I want to be able to do is have my mock bloc emit a state in response to this, such that the widget's BlocConsumer's listener will see the state change the navigate.  As you can see from the comment in the code I've tried a few things without luck.  Current nothing I've tried results in the listener seeing a state change.</p>
  <pre><code>import 'package:bloc_test/bloc_test.dart';
  import 'package:flutter/material.dart';
  import 'package:flutter_test/flutter_test.dart';
  import 'package:mocktail/mocktail.dart' as mocktail;
  import 'package:get_it/get_it.dart';
  import 'package:test_bloc_issue/bloc/my_bloc.dart';
  import 'package:test_bloc_issue/first_page.dart';
  
  class MockMyBloc extends MockBloc&lt;MyEvent, MyState&gt; implements MyBloc {}
  class FakeMyState extends Fake implements MyState {}
  class FakeMyEvent extends Fake implements MyEvent {}
  
  void main() {
    MockMyBloc mockMyBloc;
    mocktail.registerFallbackValue&lt;MyState&gt;(FakeMyState());
    mocktail.registerFallbackValue&lt;MyEvent&gt;(FakeMyEvent());
    mockMyBloc = MockMyBloc();
  
    var nextScreenPlaceHolder = Container();
  
    setUpAll(() async {
      final di = GetIt.instance;
      di.registerFactory&lt;MyBloc&gt;(() =&gt; mockMyBloc);
    });
  
    _loadScreen(WidgetTester tester) async {
      mocktail.when(() =&gt; mockMyBloc.state).thenReturn(FirstState());
      await tester.pumpWidget(
        MaterialApp(
          home: FirstPage(),
          routes: &lt;String, WidgetBuilder&gt; {
            'SECONDPAGE': (context) =&gt; nextScreenPlaceHolder
          }
        )
      );
    }
  
    testWidgets('test', (WidgetTester tester) async {
      await _loadScreen(tester);
      await tester.tap(find.byType(ElevatedButton));
      await tester.pump();
      
      // What do I need to do here to mock the state change that would
      // happen in the real bloc when a TriggerStateChange event is received,
      // such that the listener in my BlocConsumer will see it?
      // if tried:
      // whenListen(mockMyBloc, Stream&lt;MyState&gt;.fromIterable([SecondState()]));
      // and
      // mocktail.when(() =&gt; mockMyBloc.state).thenReturn(SecondState());
      await tester.pumpAndSettle();
  
      expect(find.byWidget(nextScreenPlaceHolder), findsOneWidget);
    });
  }
  </code></pre>