Testing my bloc fails when using yield and Either in the mapEventToState

1,023

So I solved it my self. The code in the question is valid for older versions of dart. At present

bloc.state

will break the code. Instead

bloc

should be used to get the state.

The second problem is that it's no longer possible to have a

throw UnimplementedError();

in the fold method of the Either object. That will cause an exception to be thrown in either case. There has to be some non exception logic in the right function of the fold call.

The modifications are reflected in the code below.

number_trivia_bloc_test.dart

test('should emit [Error] when the input is invalid', () async {
  when(mockInputConverter.stringToUnsignedInteger(any))
      .thenReturn(Left(InvalidInputFailure()));

  bloc.add(GetTriviaForConcreteNumberEvent(tNumberString));

  final expected = [
    Error(message: invalidInput),
  ];
  expectLater(bloc, emitsInOrder(expected));
});

number_trivia_bloc.dart

@override
Stream<NumberTriviaState> mapEventToState(NumberTriviaEvent event,
  ) async* {
  if (event is GetTriviaForConcreteNumberEvent) {
    final inputEither =
      inputConverter.stringToUnsignedInteger(event.numberString);

    yield* inputEither.fold(
      (failure) async* {
        yield Error(message: invalidInput);
      },
      (integer) async* {
        yield null;
      },
    ); 
  }
}
Share:
1,023
Mohamed Haidar
Author by

Mohamed Haidar

Updated on December 23, 2022

Comments

  • Mohamed Haidar
    Mohamed Haidar over 1 year

    I'm following a course on clean architecture and I'm stuck with the following error.

    The following test fails.

    test('should emit [Error] when the input is invalid', () async {
      final tNumberString = '1';
      when(mockInputConverter.stringToUnsignedInteger(any))
        .thenReturn(Left(InvalidInputFailure()));
    
      final expected = [
        Empty(),
        Error(message: invalidInput),
       ];
       expectLater(bloc.state, emitsInOrder(expected));
    
       bloc.add(GetTriviaForConcreteNumberEvent(tNumberString));
    });
    

    My NumberTriviaBloc is as follows

    part 'number_trivia_event.dart';
    part 'number_trivia_state.dart';
    
    const String serverFailureMessage = 'Server Failure';
    const String cacheFailureMessage = 'Cache Failure';
    const String invalidInput =
        'Invalid input - the number should be a positive integer';
    
    class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
      NumberTriviaBloc(
          {@required GetConcreteNumberTrivia concrete,
          @required GetRandomNumberTrivia random,
          @required this.inputConverter})
          : assert(concrete != null),
            assert(random != null),
            assert(inputConverter != null),
            _getConcreteNumberTrivia = concrete,
            _getRandomNumberTrivia = random,
            super(Empty());
    
      final GetConcreteNumberTrivia _getConcreteNumberTrivia;
      final GetRandomNumberTrivia _getRandomNumberTrivia;
      final InputConverter inputConverter;
    
      @override
      Stream<NumberTriviaState> mapEventToState(
        NumberTriviaEvent event,
      ) async* {
        if (event is GetTriviaForConcreteNumberEvent) {
          final inputEither =
              inputConverter.stringToUnsignedInteger(event.numberString);
    
          yield* inputEither.fold(
            (l) async* {
              yield Error(message: invalidInput);
            },
            (r) => throw UnimplementedError(),
          );
        }
      }
    }
    

    Im not that familiar with the bloc patern and the testing mechanisms of streams and states. Im guessing somehow the code isn't executed correctly because the fail message is as follows

    ERROR: Expected: should do the following in order:
    emit an event that Empty:<Empty>
    emit an event that Error:<Error>
    Actual: Empty:<Empty>
    Which: was not a Stream or a StreamQueue