flutter 在mapEventToState中使用yield和Either时,测试我的块失败

2ledvvac  于 2023-03-13  发布在  Flutter
关注(0)|答案(2)|浏览(138)

我正在学习一门关于清洁架构的课程,但遇到了以下错误。
以下测试失败。

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

我的数字琐事区块如下

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

我对块模式和流与状态的测试机制不是很熟悉,我猜代码可能没有正确执行,因为失败消息如下所示

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
hlswsv35

hlswsv351#

所以我自己解决了这个问题。问题中的代码对dart的旧版本有效。目前

bloc.state

会破译密码。相反

bloc

应该被用来获取状态。
第二个问题是,它不再可能有一个

throw UnimplementedError();

在Either对象的fold方法中。这将导致在任何情况下抛出异常。在fold调用的正确函数中必须有一些非异常逻辑。
修改反映在下面的代码中。
编号_琐事_块_测试.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));
});

编号_琐事_块.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;
      },
    ); 
  }
}
u4dcyp6a

u4dcyp6a2#

我希望这个问题仍然具有现实意义。
实际上,没有必要从头开始写流和yield语句,就像新版本的bloc一样。但是当我们在测试中设置Later调用时,我们需要在期望结果的同时解析一个块流。所以,代码的解决方案是使用bloc.stream而不是bloc.state,它将块转换为块流并按顺序输出。请记住,流不包含初始/EmptyState(),但所有其他事件都按顺序处理,因此发出相同的事件。请参考以下代码片段以获得正确的实现,

This is the bloc file that contains the bloc logic as per the latest versions.

///<inputs an event, outputs the state> => <NumberTriviaEvent, 
NumberTriviaState>
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
  final GetConcreteNumberTrivia concreteNumberTrivia;
  final GetRandomNumberTrivia randomNumberTrivia;
  final InputConverter inputConverter;

  NumberTriviaBloc(
      {required this.concreteNumberTrivia,
      required this.randomNumberTrivia,
      required this.inputConverter}) : super(EmptyState()) {
    on<NumberTriviaEvent>((event, emit) async{
        if(event is GetTriviaForConcreteNumber){
          final input = inputConverter.stringToUnsignedInteger(event.numberString);
          input.fold((lFailure){
             emit( const ErrorState(errorMessage: INVALID_INPUT_FAILURE_MESSAGE));
          }, (rSuccess) {
             emit(const LoadedState(numberTrivia: NumberTrivia(text: 'sample test', number: 1)));
          });
        }
    });
  }
}

This is the test file that tests the output over the block that is written above.

    test('should emit [Error] when theres an invalid input', () {
      //  arrange
      when(inputConverter.stringToUnsignedInteger('abc'))
          .thenAnswer((realInvocation) => Left(InvalidInputFailure()));

      //  assert Later
      /// As we will be checking if the emit contains the expected result, we will use expectLater() method in test
      /// which puts the particular test on hold until the data is been emitted(upper limit 30 seconds) & then applies the checks
      expectLater(
        bloc.stream,
        emitsInOrder([
          const ErrorState(errorMessage: INVALID_INPUT_FAILURE_MESSAGE)
          ]));

      //  act
      bloc.add(const GetTriviaForConcreteNumber(numberString: 'abc'));
    });
  });

Learning Source: Reso Coders

相关问题