如何使用 Firebase Cloud Firestore 对方法进行单元测试?

Posted

技术标签:

【中文标题】如何使用 Firebase Cloud Firestore 对方法进行单元测试?【英文标题】:How to unit test methods using Firebase Cloud Firestore? 【发布时间】:2019-02-15 12:36:57 【问题描述】:

我正在尝试在 Flutter 上使用 Cloud Firestore。到目前为止,我已经能够使用它,但我想在我的项目中包含使用 firestore 的功能的单元测试。我正在使用 Mockito 做同样的事情,我认为它应该可以工作。

但我真的对 Dart 的 StreamMatchers 感到困惑。我无法理解错误消息。

下面是我想要实现的一些代码以及我遇到的错误。

repository.dart

    import 'dart:async';

    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:sunapsis_conference18/models/conference_event.dart';

    Stream<List<ConferenceEvent>> getAllEvents() 
        return _firestore
            .collection(_collectionName)
            .snapshots()
            .map((QuerySnapshot snapshot) => _eventMapper(snapshot));
    
  

    List<ConferenceEvent> _eventMapper(QuerySnapshot snapshot) 
        List<ConferenceEvent> events = [];
        for (int i = 0; i < snapshot.documents.length; i++) 
          DocumentSnapshot documentSnapshot = snapshot.documents[i];
          ConferenceEvent event =
              ConferenceEvent.buildFromMap(documentSnapshot.data);
          events.add(event);
        
        return events;
    

It 方法尝试从 firestore 集合中获取数据并返回数据对象列表流。

repository_test.dart

    import 'dart:async';

    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:mockito/mockito.dart';
    import 'package:sunapsis_conference18/models/conference_event.dart';
    import 'package:sunapsis_conference18/repository/repository.dart'; //quotation mark fix    
    import 'package:test/test.dart';

    class MockDocumentReference extends Mock implements DocumentReference 
    
    class MockFirestore extends Mock implements Firestore 
    
    class MockCollectionReference extends Mock implements CollectionReference 
    
    class MockQuerySnapshot extends Mock implements QuerySnapshot 
    
    class MockDocumentSnapshot extends Mock implements DocumentSnapshot 
    
    class MockQuery extends Mock implements Query 
    
    main() 
      group('getAllEvents() tests', () 
        final Firestore mockFirestore = MockFirestore();
        final CollectionReference mockCollectionReference =
            MockCollectionReference();
        final QuerySnapshot mockQuerySnapshot = MockQuerySnapshot();
        final DocumentSnapshot mockDocumentSnapshot = MockDocumentSnapshot();
        final Repository repository = Repository(mockFirestore);
        final DocumentReference _mockDocumentRef = MockDocumentReference();
        final Map<String, dynamic> _responseMap = 
          'foo': 123,
          'bar': 'Test title',
        ;
        final ConferenceEvent _event = ConferenceEvent.buildFromMap(_responseMap);
    
        test('returns correct stream of list of ConferenceEvent', () async 
          when(mockFirestore.collection('events'))
              .thenReturn(mockCollectionReference);
          when(mockCollectionReference.snapshots())
              .thenAnswer((_) => Stream.fromIterable([mockQuerySnapshot]));
    
          when(mockQuerySnapshot.documents).thenReturn([mockDocumentSnapshot]);
          when(mockDocumentSnapshot.data).thenReturn(_responseMap);
          await expectLater(
              repository.getAllEvents(),
              emitsAnyOf([
                [_event],
                emitsDone
              ]));
        );
      );
    

我无法制定正确的流匹配器,同时也无法理解错误消息。

错误

Expected: should do one of the following:
          • emit an event that [Instance of 'ConferenceEvent']
          • be done
  Actual: <Instance of '_MapStream<QuerySnapshot, List<ConferenceEvent>>'>
   Which: emitted • [Instance of 'ConferenceEvent']
                  x Stream closed.
            which failed all options:
                  • failed to emit an event that [Instance of 'ConferenceEvent'] because it emitted an event that was <Instance of 'ConferenceEvent'> instead of <Instance of 'ConferenceEvent'> at location [0]
                  • failed to be done

非常感谢任何有助于理解错误消息的指导。另外,这是测试此功能的正确方法吗?

【问题讨论】:

你能从测试文件的顶部添加导入吗? @GünterZöchbauer,我已经添加了导入。在阅读您的评论并尝试再次添加导入后,我认为这是一个导入问题,但它没有解决。非常感谢您对此进行调查。 看起来不错。不知道是什么原因造成的。 现在我很担心,因为我很确定你能帮上忙。 ailed to emit an event that [Instance of 'ConferenceEvent'] because it emitted an event that was &lt;Instance of 'ConferenceEvent'&gt; instead of &lt;Instance of 'ConferenceEvent'&gt; at location [0] 这没有多大意义,尤其是 location[0] 部分。 这是一个如何对 Cloud Firestore dart 代码进行单元测试的示例,可能会有所帮助:github.com/brianegan/flutter_architecture_samples/blob/master/… 【参考方案1】:

我认为问题与expectLater 比较ConferenceEvent 实例的方式有关。它使用==(相等)运算符来比较这两个对象,并且在不覆盖ConferenceEvent 中的相等运算符的情况下,这些对象不会具有相同的hashCode。

在您模拟的代码中,这里实例化了一个新对象

ConferenceEvent event =
          ConferenceEvent.buildFromMap(documentSnapshot.data);

稍后你将它与在此行中创建的另一个 ConferenceEvent 对象进行比较:

final ConferenceEvent _event = ConferenceEvent.buildFromMap(_responseMap);

【讨论】:

我遇到了类似的问题,这个解决方案奏效了。我只需要覆盖 == 运算符和 hashCode。

以上是关于如何使用 Firebase Cloud Firestore 对方法进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 Firebase Cloud Messaging 控制台的情况下向 Flutter/Firebase Cloud Messaging 上的特定设备发送通知 [重复]

如何使用 Cloud Functions for Firebase 更新 Firebase 实时数据库中的值

如何使用 PHP 在 Firebase 中自动导入存储桶 - Google Cloud Storage

如何使用 Firebase Cloud Messaging 在前台显示多个通知

如果用户长时间没有写入,如何使用 Firebase Cloud Functions 在 Firebase 数据库中设置节点的值?

如何使用 Firebase Cloud Messaging 向所有用户发送数据负载?