Flutter异步测试辅助插件bloc_test_async的使用

Flutter异步测试辅助插件bloc_test_async的使用

bloc_test_async 是一个用于测试异步操作的 bloc 扩展插件。它通过等待事件完成来替代使用静态的 wait 参数,从而使得测试更加灵活和可靠。

问题

bloc_test 提供了一个 wait 参数来延迟执行 expect 块以测试异步操作。但是,当异步操作所需时间超过 wait 持续时间时,测试会失败,因为所有在 expected 中定义的状态没有按时被发出。

因此,wait 参数需要设置为一个静态的、估计的时间。这在任何影响实际持续时间的变化发生时都可能需要调整 wait 参数,这带来了问题。

解决方案

使用 bloc_test_async 包来弃用 wait 参数,并改用等待事件完成的方式。这样可以更准确地测试异步操作。

blocTest("ExampleBloc",
    build: () => ExampleBloc(),
    act: (bloc) async {
      await bloc.addToComplete(ExampleEvent());
    },
    expect: () => [ExampleLoadingState(), ExampleSuccessState()]);

addToComplete 扩展方法向 bloc 添加一个事件,并返回一个 Future 来等待该事件在 bloc 中完成。因此,不需要 wait 参数,因为可以确保在 act 块中添加的事件已经完成。

迁移指南

添加依赖

pubspec.yaml 文件中添加 bloc_test_async 依赖:

dependencies:
  bloc_test_async: ^0.1.0

修改Bloc实现

你的 bloc 实现需要进行调整以便使用 addToComplete 方法。只需将 on 方法替换为 completeOn

class MyBloc extends Bloc<Event, State> {
  MyBloc() : super(Initial()) {
    // 替换之前的 on&lt;Event&gt;((event, emit)
    completeOn&lt;Event&gt;((event, emit) async {
      emit(Loading());
      // 执行一些操作
      emit(Success());
    });
  }
}

completeOn 在非测试环境中运行时,会跳过事件完成的信号处理逻辑,行为类似于 on 方法。

修改blocTest

blocTest 中,如果需要,可以删除 wait 参数,并使用 await addToComplete() 替换 add() 方法。act 块必须定义为 async 以使用 await

blocTest("MyBloc",
    build: () => MyBloc(),
    act: (bloc) async {
      // 替换之前的 bloc.add(Event())
      await bloc.addToComplete(Event());
    },
    expect: () => [Loading(), Success()]);

addToComplete 在非测试环境中使用时,会抛出异常。

超时处理

addToComplete 方法提供了 timeout 参数来定义事件完成的超时时间。

blocTest("MyBloc",
    build: () => MyBloc(),
    act: (bloc) async {
      await bloc.addToComplete(Event(), timeout: Duration(seconds: 3));
    },
    expect: () => [Loading(), Success()]);

默认情况下,超时时间为 5 秒。如果超时发生,则会抛出 TimeoutException 并导致测试失败。

完整示例

以下是一个完整的示例代码,展示了如何使用 bloc_test_async 插件进行异步测试:

import 'dart:math';

import 'package:bloc/bloc.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:bloc_test_async/bloc_test_async.dart';
import 'package:equatable/equatable.dart';

// 定义事件类型
class RandomDelayEvent {}

// 定义状态类型
sealed class State extends Equatable {
  [@override](/user/override)
  List<Object?> get props => [];
}

class Initial extends State {}

class Loading extends State {}

class Success extends State {}

// 定义Bloc类
class RandomDelayBloc extends Bloc<RandomDelayEvent, State> {
  RandomDelayBloc() : super(Initial()) {
    // 使用completeOn替换on方法
    completeOn<RandomDelayEvent>((event, emit) async {
      emit(Loading());

      // 随机生成一个延迟时间
      Duration delay = Duration(milliseconds: Random().nextInt(2000));
      await Future.delayed(delay);

      emit(Success());
    });
  }
}

void main() {
  // 使用blocTest进行测试
  blocTest("RandomDelayBloc",
      build: () => RandomDelayBloc(),
      act: (bloc) async {
        await bloc.addToComplete(RandomDelayEvent());
      },
      expect: () => [Loading(), Success()]);
}

更多关于Flutter异步测试辅助插件bloc_test_async的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter异步测试辅助插件bloc_test_async的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用bloc_test_async插件来进行异步测试的示例。bloc_test_async插件通常用于与bloc库一起测试Flutter应用中的业务逻辑状态管理。

首先,确保你的pubspec.yaml文件中包含以下依赖项:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0  # 确保使用兼容版本

dev_dependencies:
  test: ^1.17.0  # 确保使用兼容版本
  bloc_test: ^9.0.0  # bloc_test通常包含异步测试支持

注意:bloc_test本身已经包含了对异步测试的支持,通常不需要单独使用bloc_test_async,但这里我们会展示如何使用bloc_test进行异步测试,因为bloc_test已经足够强大。

示例代码

假设我们有一个简单的计数器Bloc:

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

part 'counter_event.dart';
part 'counter_state.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterInitial());

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    if (event is CounterIncremented) {
      yield* _mapCounterIncrementedToState();
    }
  }

  Stream<CounterState> _mapCounterIncrementedToState() async* {
    yield CounterState(count: state.count + 1);
    // 模拟异步操作,例如等待API调用
    await Future.delayed(Duration(milliseconds: 100));
  }
}

counter_event.dart

part of 'counter_bloc.dart';

abstract class CounterEvent extends Equatable {
  const CounterEvent();

  @override
  List<Object> get props => [];
}

class CounterIncremented extends CounterEvent {}

counter_state.dart

part of 'counter_bloc.dart';

class CounterState extends Equatable {
  final int count;

  const CounterState({required this.count});

  @override
  List<Object> get props => [count];
}

class CounterInitial extends CounterState {
  const CounterInitial() : super(count: 0);
}

现在,我们来编写一个测试,使用bloc_test库来测试这个异步计数器Bloc:

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test/test.dart';
import 'counter_bloc.dart';

void main() {
  group('CounterBloc', () {
    blocTest<CounterBloc, CounterState>(
      'emits [CounterInitial, CounterState] when CounterIncremented is added',
      build: () => CounterBloc(),
      act: (bloc) => bloc.add(CounterIncremented()),
      expect: [
        CounterInitial(),
        isA<CounterState>().having((state) => state.count, 'count', 1),
      ],
      // 使用wait来等待异步操作完成
      wait: const Duration(milliseconds: 200), // 确保这个时间大于模拟的异步延迟时间
    );
  });
}

在这个测试案例中,我们使用了blocTest函数来测试CounterBlocwait参数用于等待异步操作完成,这里我们设置了一个稍微大于模拟异步延迟的时间(100毫秒)。

运行这个测试,你应该会看到测试通过,表明我们的计数器Bloc在接收到CounterIncremented事件后,正确地发射了初始状态和更新后的状态。

注意:确保你的测试环境配置正确,并且所有依赖项都已正确安装。在Flutter项目中,通常使用flutter test命令来运行测试。

回到顶部