Bloc中使用decodeImageFromList后add事件在blocTest测试中不生效问题
原生方法测试环境依赖缺失
decodeImageFromList是 Flutter 封装的原生侧图片解码API,依赖系统原生的图片解码能力。纯Dart单元测试环境没有原生运行时支撑,该方法的await会永久阻塞,后续的add逻辑永远不会被执行。就算是在Flutter Widget测试环境下,blocTest默认只会等待Dart虚拟机内部的微任务、普通Future队列执行完成,不会主动等待原生方法通道的回调结果,也会导致add逻辑没有被捕获。Bloc事件处理上下文提前销毁
你当前的_mapEventOne是async*类型的生成器函数,整个函数没有任何yield语句,Bloc监听到该函数返回的Stream没有任何状态输出,会直接判定该事件处理流程结束,销毁对应的处理上下文。等decodeImageFromList的异步回调返回时,已经没有有效的执行环境可以触发后续的add操作。
方案1:Mock图片解码逻辑,规避原生依赖
将图片解码逻辑抽象为独立依赖注入到Bloc中,测试时使用Mock实现直接返回预设的宽高参数,不需要调用真实的原生解码方法:// 抽象解码依赖 abstract class ImageDecoder { Future<ui.Image> decode(Uint8List bytes); } // 真实实现,生产环境使用 class DefaultImageDecoder implements ImageDecoder { @override Future<ui.Image> decode(Uint8List bytes) => decodeImageFromList(bytes); } // Mock实现,测试环境使用 class MockImageDecoder extends Mock implements ImageDecoder { @override Future<ui.Image> decode(Uint8List bytes) async => MockImage(width: 1080, height: 1920); }Bloc构造时传入对应的解码器实现即可,测试时不会触发真实的原生调用,异步逻辑完全可控。
方案2:保持事件处理上下文活跃
如果需要保留内部add事件的写法,可以在异步等待前先yield一个过渡状态,让Bloc知道当前事件处理流程还未结束,不会提前销毁上下文:Stream<OurState> _mapEventOne(File file) async* { yield OurState.imageProcessing(); // 先输出过渡状态,保持Stream活跃 final decodedImage = await decodeImageFromList(await pickedImage.readAsBytes()); add( EventTwo( width: decodedImage.width, height: decodedImage.height, ), ); }方案3:调整逻辑避免内部调用add
Bloc官方不推荐在事件处理函数内部通过add触发其他事件,更建议直接调用对应事件的处理逻辑,减少异步调度的不可控因素:Stream<OurState> _mapEventOne(File file) async* { final decodedImage = await decodeImageFromList(await pickedImage.readAsBytes()); // 直接调用EventTwo的处理逻辑,不需要走事件调度 yield* _mapEventTwo( width: decodedImage.width, height: decodedImage.height, ); }方案4:调整测试等待逻辑(集成测试场景)
如果是集成测试必须使用真实解码能力,可以给blocTest的wait参数设置合理的等待时长,或者在Widget测试中调用tester.pumpAndSettle()等待原生回调执行完成后再做断言。
内容的提问来源于stack exchange,提问作者xkxeeshankhan

