单元测试Firestore相关方法时遇PlatformException问题求助
PlatformException(channel-error)问题 问题场景
在对调用Firestore的loadMedias方法进行单元测试时,触发以下错误:
PlatformException(channel-error, Unable to establish connection on channel., null, null)
相关代码
待测试方法:
Future<MediasState> loadMedias(AbstractEvent event) async { late AbstractBlocState streamState; try { DataHelper _dataHelperMediasEvent = DataHelperFactory.instance .createInstanceFromAnotherDataHelperAndEntityInstance( _dataHelperEvents, event, 'medias'); List<AbstractMedia> medias = (await _dataHelperMediasEvent.getAll()).cast<AbstractMedia>(); for (AbstractMedia media in medias) { media.user = await (_dataHelperUsers.getEntity(media.user.get())); } medias = sortEntitiesByCreatedDateDesc(medias) as List<AbstractMedia>; streamState = MediasShowed(medias); } catch (error) { streamState = MediasShowedError(); } finally { return streamState as MediasState; } }
测试代码:
test('Test load medias', () async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); AbstractEvent event = EntityFactory.createInstance('Event') as AbstractEvent; AbstractMedia media1 = EntityFactory.createInstance('Media') as AbstractMedia; AbstractMedia media2 = EntityFactory.createInstance('Media') as AbstractMedia; AbstractMedia media3 = EntityFactory.createInstance('Media') as AbstractMedia; List<AbstractMedia> listOfMedias = <AbstractMedia>[]; listOfMedias.add(media1); listOfMedias.add(media2); listOfMedias.add(media3); when(mockMediasEvent.loadMedias(event)) .thenAnswer((_) async => MediasShowed(listOfMedias)); blocMedias.add(LoadMediaOfOneEvent(event)); await expectLater(blocMedias.state, isA<MediasShowedError>()); });
DataHelper类:
class DataHelper { DataHelper.initialize(String collectionPath) { this.setCollection(collectionPath); } late String collectionPath; late String className; late CollectionReference<Map<String, dynamic>> collection; final FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance; }
解决思路
修改
DataHelper支持依赖注入
当前DataHelper硬编码依赖FirebaseFirestore.instance,导致单元测试无法替换为Mock实例。修改构造函数,允许传入FirebaseFirestore实例:class DataHelper { final FirebaseFirestore firebaseFirestore; DataHelper.initialize(String collectionPath, {FirebaseFirestore? firestore}) : firebaseFirestore = firestore ?? FirebaseFirestore.instance { this.setCollection(collectionPath); } // ... 其他原有代码 }这样测试时可以传入Mock的Firestore实例,避免连接真实服务。
使用Firestore Mock库替代真实实例
引入cloud_firestore_mocks包,它提供了无需原生通道的MockFirebaseFirestore实现。在测试中初始化Mock实例并注入到DataHelper,模拟Firestore操作:test('Test load medias', () async { // 初始化Mock Firestore final mockFirestore = MockFirebaseFirestore(); // 创建Mock的DataHelper实例,注入mockFirestore final mockDataHelperMedias = DataHelper.initialize('medias', firestore: mockFirestore); final mockDataHelperUsers = DataHelper.initialize('users', firestore: mockFirestore); // 模拟getAll方法返回预期媒体列表 when(mockDataHelperMedias.getAll()).thenAnswer((_) async => listOfMedias); // 模拟getEntity方法返回用户实例 when(mockDataHelperUsers.getEntity(any)).thenAnswer((_) async => mockUser); // 注入Mock实例到bloc或DataHelperFactory // ... blocMedias.add(LoadMediaOfOneEvent(event)); await expectLater(blocMedias.state, isA<MediasShowed>()); });修正测试中的Mock逻辑
当前测试Mock的是mockMediasEvent.loadMedias(event),但实际loadMedias方法内部调用的是_dataHelperMediasEvent.getAll()和_dataHelperUsers.getEntity(),这些依赖未被Mock,导致触发真实Firestore调用。需要确保_dataHelperMediasEvent和_dataHelperUsers都是Mock实例,并正确Stub它们的方法。移除测试中真实Firebase初始化
单元测试无需连接真实Firebase服务,直接删除await Firebase.initializeApp();,避免尝试建立原生通道连接。
内容的提问来源于stack exchange,提问作者Esca

