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

单元测试Firestore相关方法时遇PlatformException问题求助

解决Firestore单元测试中的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 20:24:31