Bloc: Exception thrown by bloc not caught by bloc_test

Created on 8 May 2020  路  4Comments  路  Source: felangel/bloc

Hi @felangel 馃憢

Really appreciate all your effort in resolving #1122, but I am still facing the error. Replicated the error using code snippet from flutter_login example.

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final UserRepository userRepository;
  final AuthenticationBloc authenticationBloc;

  LoginBloc({
    @required this.userRepository,
    @required this.authenticationBloc,
  })  : assert(userRepository != null),
        assert(authenticationBloc != null);

  @override
  LoginState get initialState => LoginInitial();

  @override
  Stream<LoginState> mapEventToState(LoginEvent event) async* {
    if (event is LoginButtonPressed) {
      yield LoginLoading();
      try {
        final token = await userRepository.authenticate(
          username: event.username,
          password: event.password,
        );
        authenticationBloc.add(LoggedIn(token: token));
        yield LoginInitial();
      } on SocketException {
        rethrow;
      } catch (error) {
        yield LoginFailure(error: error.toString());
      }
    }
  }
}

Corresponding failing bloc test:

    blocTest('emits [LoginLoading, LoginFailure] on failure',
        build: () async {
          when(userRepository.authenticate(
            username: 'valid.username',
            password: 'valid.password',
          )).thenThrow(SocketException('test exception'));
          return loginBloc;
        },
        act: (bloc) => bloc.add(
              LoginButtonPressed(
                username: 'valid.username',
                password: 'valid.password',
              ),
            ),
        expect: [
          LoginLoading(),
        ],
        verify: (_) async {
          verifyNever(authenticationBloc.add(any));
        },
        errors: [isA<SocketException>()]);
  });

Test passing with alternate implementation

  test('emits [LoginLoading, LoginFailure] on failure 2', () async {
    runZoned(() {
      when(userRepository.authenticate(
        username: 'valid.username',
        password: 'valid.password',
      )).thenThrow(SocketException('test exception'));
      LoginBloc loginBloc = LoginBloc(
        userRepository: userRepository,
        authenticationBloc: authenticationBloc,
      );

      final expectedResponse = [
        LoginInitial(),
        LoginLoading(),
      ];
      expectLater(
        loginBloc,
        emitsInOrder(expectedResponse),
      );
      loginBloc.add(LoginButtonPressed(
        username: 'valid.username',
        password: 'valid.password',
      ));
    }, onError: (error, stackTrace) {
      if (error is BlocUnhandledErrorException) {
        expect(error.error, isA<SocketException>());
      }
    });
  });

Please correct me if I am wrong. Here SocketException should have been caught and verified in the errors but get the follwoing error in the logs. This is in continuation of the logic mentioned in #1122 that SocketException would be later propagated to BlocDelegate to be reported.


Error log
C:\src\flutter\bin\flutter.bat --no-color test --machine --plain-name "emits [LoginLoading, LoginFailure] on failure" test\unit\login\login_bloc_test.dart
package:bloc/src/bloc.dart 146:7 Bloc.onError.
package:bloc/src/bloc.dart 147:6 Bloc.onError
===== asynchronous gap ===========================
dart:async _StreamImpl.listen
package:bloc/src/bloc.dart 260:7 Bloc._bindEventsToStates
package:bloc/src/bloc.dart 59:5 new Bloc
package:flutter_login/login/bloc/login_bloc.dart new LoginBloc
test\unit\login\login_bloc_test.dart 22:17 main.

Unhandled error SocketException: test exception occurred in bloc Instance of 'LoginBloc'.

0 PostExpectation.thenThrow. (package:mockito/src/mock.dart:346:7)

1 Mock.noSuchMethod (package:mockito/src/mock.dart:123:45)

2 LoginBloc.mapEventToState (package:flutter_login/login/bloc/login_bloc.dart:31:44)

3 Bloc._bindEventsToStates. (package:bloc/src/bloc.dart:252:20)

4 Stream.asyncExpand.onListen. (dart:async/stream.dart:579:30)

5 StackZoneSpecification._registerUnaryCallback.. (package:stack_trace/src/stack_zone_specification.dart:129:26)

6 StackZoneSpecification._run (package:stack_trace/src/stack_zone_specification.dart:209:15)

7 StackZoneSpecification._registerUnaryCallback. (package:stack_trace/src/stack_zone_specification.dart:129:14)

8 _rootRunUnary (dart:async/zone.dart:1196:13)

9 _CustomZone.runUnary (dart:async/zone.dart:1085:19)

10 _CustomZone.runUnaryGuarded (dart:async/zone.dart:987:7)

11 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)

12 _DelayedData.perform (dart:async/stream_impl.dart:594:14)

13 _StreamImplEvents.handleNext (dart:async/stream_impl.dart:710:11)

14 _PendingEvents.schedule. (dart:async/stream_impl.dart:670:7)

15 StackZoneSpecification._run (package:stack_trace/src/stack_zone_specification.dart:209:15)

16 StackZoneSpecification._registerCallback. (package:stack_trace/src/stack_zone_specification.dart:119:48)

17 _rootRun (dart:async/zone.dart:1180:38)

18 _CustomZone.run (dart:async/zone.dart:1077:19)

19 _CustomZone.runGuarded (dart:async/zone.dart:979:7)

20 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1019:23)

21 StackZoneSpecification._run (package:stack_trace/src/stack_zone_specification.dart:209:15)

22 StackZoneSpecification._registerCallback. (package:stack_trace/src/stack_zone_specification.dart:119:48)

23 _rootRun (dart:async/zone.dart:1184:13)

24 _CustomZone.run (dart:async/zone.dart:1077:19)

25 _CustomZone.runGuarded (dart:async/zone.dart:979:7)

26 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1019:23)

27 _microtaskLoop (dart:async/schedule_microtask.dart:43:21)

28 _startMicrotaskLoop (dart:async/schedule_microtask.dart:52:5)

bloc_test question

Most helpful comment

Hey @felangel 馃憢

Thanks a lot for all your effort. 馃挴

All 4 comments

Hi @MohsinN 馃憢
Thanks for opening an issue!

Are you able to share a link to a sample app which reproduces the issue so I can debug it locally? Thanks 馃憤

Hi @felangel 馃憢

Please find the project in this link.

Relevant changes are in:

  1. login_bloc.dart: line 37-38
  2. login_bloc_test.dart: line 92 onwards

Hi @MohsinN 馃憢
Sorry for the delay! I took a look and the issue is in your blocTest you are not actually creating the bloc inside of the build so the bloc creation is not part of the test zone.

If you update your test to:

    blocTest(
      'emits [LoginLoading, LoginFailure] on failure',
      build: () async {
        when(userRepository.authenticate(
          username: 'valid.username',
          password: 'valid.password',
        )).thenThrow(SocketException('test exception'));
        return LoginBloc(
          userRepository: userRepository,
          authenticationBloc: authenticationBloc,
        );
      },
      act: (bloc) => bloc.add(
        LoginButtonPressed(
          username: 'valid.username',
          password: 'valid.password',
        ),
      ),
      expect: [
        LoginLoading(),
      ],
      verify: (_) async {
        verifyNever(authenticationBloc.add(any));
      },
      errors: [isA<SocketException>()],
    );

It will work as expected 馃憤

Hope that helps!

Hey @felangel 馃憢

Thanks a lot for all your effort. 馃挴

Was this page helpful?
0 / 5 - 0 ratings