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的方法匹配部分。
修改步骤
- 补充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> {}
- 调整测试初始化逻辑,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); });
- 删除所有对被测类自身方法的Stub:去掉所有
when(() => dataSource.getAll())相关代码,只需要保留storage.readAll的Stub即可,getAll的真实逻辑会基于你Stub的readAll返回正确结果。 - 补全所有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); });
- 异常测试的校验逻辑调整,确保异常被正确捕获:
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
相关产品推荐
相关产品推荐

