How to implement widget tests by using MockBloc?

3,379

I solved the problem, I would like to share the answer, in case someone finds out the same problem.

First of all, this link was really helpful.

The solution was to change the Widget Test in the following way:

...
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
...

class MockAuthenticationBloc
    extends MockBloc<AuthenticationEvent, AuthenticationState>
    implements AuthenticationBloc {}

class AuthenticationStateFake extends Fake implements AuthenticationState {}

class AuthenticationEventFake extends Fake implements AuthenticationEvent {}

void main() {
  group('Login', () {

    setUpAll(() {
      registerFallbackValue<AuthenticationState>(AuthenticationStateFake());
      registerFallbackValue<AuthenticationEvent>(AuthenticationEventFake());
    });

    testWidgets(
        'should show a Message when the Authentication state is Error',
        (WidgetTester tester) async {
      // arrange
      final mockAuthenticationBloc = MockAuthenticationBloc();
      when(() => mockAuthenticationBloc.state).thenReturn(
        LoggingIn(), // the desired state
      );

      // find
      final widget = LoginForm();
      final messageWidget = find.byType(Message);

      // test
      await tester.pumpWidget(
         BlocProvider<AuthenticationBloc>(
          create: (context) => mockAuthenticationBloc,
          child: MaterialApp(
            title: 'Widget Test',
            home: Scaffold(body: widget),
          ),
        ),
      );
      await tester.pumpAndSettle();

      // expect
      expect(messageWidget, findsOneWidget);
    });
  });
}
Share:
3,379
Yunet Luis Ruíz
Author by

Yunet Luis Ruíz

Updated on December 01, 2022

Comments

  • Yunet Luis Ruíz
    Yunet Luis Ruíz over 1 year

    I'm trying to implement a Widget Test in order to test a login form. This test depends on a bloc which I'm mocking by using MockBloc. However, it throws the following error:

    ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK╞════════════════════════════════════════════════════
    The following StateError was thrown running a test:
    Bad state: No method stub was called from within `when()`. Was a real method called, or perhaps an 
    extension method?
    

    I found a similar error in the following link, but I do not see how that can help me to solve my problem.

    I also looked at the following file on gitlub, which is an example of a widget test by using bloc_test. The link can be found on the official website of the Bloc Library - specifically in Todos App in Flutter using the Bloc library.

    However, that example is using bloc_test: ^3.0.1 while I'm using bloc_test: ^8.0.0, which can be found here.

    Here is a minimal example:

    • LoginForm Widget
    class LoginForm extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Form(
          key: '_loginForm',
          child: Column(
            children: <Widget>[
               ...
               BlocConsumer<AuthenticationBloc, AuthenticationState>(
                 listener: (context, state) {
                   ...
                 },
                 builder: (context, state) {
                   if (state is AuthenticationInitial) {
                     ...
                   } else if (state is LoggingIn || state is LoggedIn) {
                     ...
                   } else if (state is Error) { 
                     return Column(
                      children: <Widget>[
                        ...
                        Message(
                          message: state.message,
                          messageContainerWidth: 290,
                          messageContainerHeight: 51,
                        ),
                        ...
                      ],
                    );
                   }
                 }
               ),
            ],
          ),
        );
      }
    }
    
    • Message Widget
    class Message extends StatelessWidget {
      final String message;
      final double messageContainerWidth;
      final double messageContainerHeight;
    
      ...
      @override
      Widget build(BuildContext context) {
        return Container(
          width: messageContainerWidth,
          height: messageContainerHeight,
          child: Center(
            child: message != ""
                ? Text(
                    message,
                    textAlign: TextAlign.center,
                    style: TextStyle(
                      color: Color.fromRGBO(242, 241, 240, 1),
                      fontSize: 15,
                    ),
                  )
                : child,
          ),
        );
      }
    }
    
    • Widget Test (I want to test that a Message is shown when the Authentication state is Error)
    ...
    import 'package:bloc_test/bloc_test.dart';
    import 'package:flutter_bloc/flutter_bloc.dart';
    import 'package:flutter_test/flutter_test.dart';
    import 'package:mocktail/mocktail.dart';
    ...
    
    // Mocking my LoginUser usecase
    class MockLoginUser extends Mock implements LoginUser {}
    
    // Mocking my bloc
    class MockAuthenticationBloc
        extends MockBloc<AuthenticationEvent, AuthenticationState>
        implements AuthenticationBloc {}
    
    class AuthenticationStateFake extends Fake implements AuthenticationState {}
    
    void main() {
      MockLoginUser mockLoginUser;
    
      setUpAll(() {
        registerFallbackValue<AuthenticationState>(AuthenticationStateFake());
      });
    
      setUp(() {
        mockLoginUser = MockLoginUser();
        authenticationBloc = AuthenticationBloc(loginUser: mockLoginUser);
      });
    
      group('Login', () {
        testWidgets(
            'should show a Message when the Authentication state is Error',
            (WidgetTester tester) async {
          whenListen(
            authenticationBloc,
            Stream.fromIterable(
              [
                LoggingIn(),
                Error(
                  message: 'Some error message',
                ),
              ],
            ),
            initialState: AuthenticationInitial(),
          );
         
          final widget = LoginForm();
          await tester.pumpWidget(
             BlocProvider<AuthenticationBloc>(
              create: (context) => authenticationBloc,
              child: MaterialApp(
                title: 'Widget Test',
                home: Scaffold(body: widget),
              ),
            ),
          );
          await tester.pumpAndSettle();
    
          final messageWidget = find.byType(Message);
          expect(messageWidget, findsOneWidget);
        });
      });
    }
    

    I will really appreciate it if someone can help me to solve the error, or can let me know another way to implement the widget tests.

    Thanks in advance!

  • Enos Okello
    Enos Okello about 2 years
    A clarification would be great... What is the purpose of Mocking fake states and fake events if you are not using them ?