bloc_test: 糟糕状态:在调用close之后不能发出新的状态

3

我在Flutter的测试部分遇到了问题,我为一个使用Cubit的简单计数器应用编写了一个测试,但当我运行所有测试时,它会给出标题中提到的错误,请问有人知道原因吗?

需要说明的是,当我逐个运行测试时,所有测试都可以成功运行,但是当我运行所有测试组时,第二个和第三个测试返回该错误...

这是我的代码:

group("Counter Cubit", () {
    CounterCubit counterCubit = CounterCubit();
    setUp(() {
      //counterCubit = CounterCubit();
    });

    tearDown(() {
      counterCubit.close();
    });

    test("The initial state for counterCubit is CounterState(counterValue: 0)",
        () {
      expect(counterCubit.state, CounterState(0, false));
    });

    blocTest(
        "The cubit should emit CounterState(counter: 1, isIncrement: true) while we call 
          counterCubit.increment() ",
        build: () => counterCubit,
        act: (CounterCubit cubit) => cubit.increment(),
        expect: () => [CounterState(1, true)]);

    blocTest(
        "The cubit should emit CounterState(counter: -1, isIncrement: false) while we call 
           counterCubit.decrement() ",
        build: () => counterCubit,
        act: (CounterCubit cubit) => cubit.decrement(),
        expect: () => [CounterState(-1, false)]);
});

我的cubit和state如下:

class CounterCubit extends Cubit<CounterState> {
  CounterCubit() : super(CounterState(0, false));

  void increment() => emit(CounterState(state.counter + 1, true));

  void decrement() => emit(CounterState(state.counter - 1, false));
}

并且状态类似于:

class CounterState extends Equatable {
  final int counter;
  final bool isIncremented;
  CounterState(this.counter, this.isIncremented);

  @override
  List<Object?> get props => [counter, isIncremented];
}
2个回答

3

bloc_test文档如下:

blocTest创建一个新的-特定的测试用例,使用给定的description。在执行act后,blocTest将处理断言bloc发出了预期状态(按顺序)。 blocTest还会确保在评估expectation之前关闭bloc流以确保不会发出其他状态。

因此,基本上当您运行:

blocTest(
  // ...
  build: () => counterCubit, // Same instance for all tests
  // ...
);

它处理您通过build参数传递的BLoC实例。因此,由于第二个测试使用了第一个测试的相同实例,所以它会抛出异常,因为实际上它已被上一个blocTest调用(在前一个测试中)关闭。
它也解释了为什么逐个运行测试可以工作,但不是组。
要修复这个问题,请在运行blocTest时传递一个新实例(通过相同的参数)。
blocTest(
  // ...
  build: () => CounterCubit(), // Create a new instance
  // ...
);

3

虽然Alex的解决方案可以完成任务,但在我看来,这不是最干净的方法,并且它没有提到你一开始就做对了。

你代码中的问题在第二行CounterCubit counterCubit = CounterCubit();

在这里,你只实例化了一次bloc,在运行第一个测试后,它将被关闭,因此在组中的其余测试中不可用。

由于bloc将在评估期望之前自动关闭,因此你需要在每个测试之前重新实例化bloc

实现这一点的最佳方法是使用setUp(() { });方法,而不是在每个测试中实例化“build”。

将bloc定义为late变量:

然后取消注释你的setUp(() { });中的代码,使其看起来像这样:

 late CounterCubit counterCubit;

  setUp(() {
         counterCubit = CounterCubit();
        });

您也不需要在 tearDown(() {... }); 方法中关闭块,因为它会在每个测试之后自动发生。(请阅读 Alex's answer 中的引用)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接