You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter/Dart使用mocktail编写DataSourceImpl单元测试报错问题咨询

问题解决

核心错误原因

  • 错误Stub被测类自身方法:你当前测试的是DocumentDataSourceImpl的真实逻辑,不能对该类自身的getAll方法做Mock Stub,你写的when(() => dataSource.getAll())会触发真实getAll方法执行,此时内部依赖还未完成Stub就会报错,也就是日志中Bad state: Cannot call when within a stub response的直接诱因。
  • 异步方法未加await:remove、save都是返回Future的异步方法,调用时不加await会导致校验逻辑先于业务逻辑执行,同时异常也无法被throwsA正确捕获。
  • 未完整Mock依赖:你只Mock了StreamController,但没有为它的sink属性做Stub,默认返回null,调用sink.add时会触发空类型异常。
  • Void方法Stub不完整:mocktail v0.1.4要求所有Stub的方法都要显式指定返回行为,哪怕是无返回值的方法也需要补充.thenAnswer((_) async {},不能只写when的方法匹配部分。

修改步骤

  1. 补充Sink的Mock类
class FlutterSecureStorageMock extends Mock implements FlutterSecureStorage {}
class StreamControllerMock<T> extends Mock implements StreamController<T> {}
// 新增Sink的Mock类
class SinkMock<T> extends Mock implements Sink<T> {}
  1. 调整测试初始化逻辑,Stub StreamController的sink属性
void main() {
  late FlutterSecureStorageMock storage;
  late StreamControllerMock<List<DocumentModel>> streamController;
  late SinkMock<List<DocumentModel>> sinkMock;
  late DocumentDataSourceImpl dataSource;

  setUp(() {
    storage = FlutterSecureStorageMock();
    streamController = StreamControllerMock();
    sinkMock = SinkMock();
    // Stub sink属性返回Mock的Sink实例
    when(() => streamController.sink).thenReturn(sinkMock);
    dataSource = DocumentDataSourceImpl(storage, streamController);
  });
  1. 删除所有对被测类自身方法的Stub:去掉所有when(() => dataSource.getAll())相关代码,只需要保留storage.readAll的Stub即可,getAll的真实逻辑会基于你Stub的readAll返回正确结果。
  2. 补全所有Void方法的Stub行为,异步方法调用加await
    以移除功能的正向测试为例修改:
test("[P] Should remove the DocumentModel on Storage", () async {
  // Arrange
  when(() => storage.readAll())
      .thenAnswer((_) async => tDocumentModelListMap);
  // 补全add方法的Stub行为
  when(() => sinkMock.add(tDocumentModelList)).thenAnswer((_) {});
  when(() => storage.delete(key: tArgument)).thenAnswer((_) async => null);

  // Act:加await等待异步方法执行完成
  await dataSource.remove(tArgument);

  // Assert
  verify(() => storage.delete(key: tArgument)).called(1);
  verify(() => sinkMock.add(tDocumentModelList)).called(1);
});
  1. 异常测试的校验逻辑调整,确保异常被正确捕获:
test(
    "[N] Should return a DocumentNotRemovedException when the call of Storage is unsuccessful.",
    () async {
  // Arrange
  when(() => storage.delete(key: tArgument)).thenThrow(
    GenericExceptionMock(),
  );

  // Act & Assert:直接对异步调用做异常校验
  expect(() async => await dataSource.remove(tArgument), throwsA(DocumentNotRemovedException()));
  verify(() => storage.delete(key: tArgument)).called(1);
  verifyNever(() => sinkMock.add(any()));
});

存储功能的测试逻辑按上述规则同步修改即可。

内容的提问来源于stack exchange,提问作者Bruno Neuman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 08:30:05