Skip to content

Instantly share code, notes, and snippets.

@utkarsh-UK
Last active January 25, 2024 08:02
Show Gist options
  • Save utkarsh-UK/1a1d2c25fad9cbbc19df5c3a26655a82 to your computer and use it in GitHub Desktop.
Save utkarsh-UK/1a1d2c25fad9cbbc19df5c3a26655a82 to your computer and use it in GitHub Desktop.
import 'package:cloud_firestore/cloud_firestore.dart';
class MockFirestore extends Mock implements FirebaseFirestore {}
class MockCollectionReference extends Mock implements CollectionReference {}
class MockDocumentReference extends Mock implements DocumentReference {}
class MockDocumentSnapshot extends Mock implements DocumentSnapshot {}
void main() {
MockFirestore instance;
MockDocumentSnapshot mockDocumentSnapshot;
MockCollectionReference mockCollectionReference;
MockDocumentReference mockDocumentReference;
setUp(() {
instance = MockFirestore();
mockCollectionReference = MockCollectionReference();
mockDocumentReference = MockDocumentReference();
mockDocumentSnapshot = MockDocumentSnapshot();
});
//Get data from firestore doc
test('should return data when the call to remote source is successful.', () async {
when(instance.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.get()).thenAnswer((_) async => mockDocumentSnapshot);
when(mockDocumentSnapshot.data()).thenReturn(responseMap);
//act
final result = await remoteDataSource.getData('user_id');
//assert
expect(result, userModel); //userModel is a object that is defined. Replace with you own model class object.
});
//Add data to firestore doc
//This demonstrates chained call like (instance.collection('path1').doc('doc1').collection('path2').doc('doc2').add(data));
//We return the same mocked objects for each call.
test('should get data from nested document.', () async {
// arrange
when(instance.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.add(any)).thenAnswer((_) async => mockDocumentReference);
when(mockDocumentReference.id).thenReturn(messageID);
when(mockDocumentSnapshot.data()).thenReturn(messageData);
//act
final result = await remoteDataSource.addData(userID, notificationID);
//assert
expect(result, notificationID);
});
//Get list of documents from firestore.
test('should get list of all the documents.', () async {
// arrange
when(instance.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.get()).thenAnswer((_) async => mockDocumentSnapshot);
when(mockDocumentSnapshot.data()).thenReturn(roomsMap);
when(mockDocumentReference.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.get()).thenAnswer((_) async => mockQuerySnapshot);
when(mockQuerySnapshot.docs).thenReturn([mockQueryDocumentSnapshot]);
when(mockQueryDocumentSnapshot.data()).thenReturn(membersMap);
//act
final result = await remoteDataSource.fetchUsers();
//assert
expect(result, users);
});
//Delete doc in firestore.
test('should delete a doc and return true.', () async {
// arrange
when(instance.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.delete()).thenAnswer((_) async => Future.value());
//act
final result = await remoteDataSource.joinSquad();
//assert
expect(result, true);
});
//Mock query methods like orderBy(), limit().
test('should return previous messages when getting data is successful.', () async {
// arrange
when(instance.collection(any)).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.collection('messages')).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(lastMessageID)).thenReturn(mockDocumentReference);
when(mockDocumentReference.get()).thenAnswer((realInvocation) async => mockDocumentSnapshot);
when(mockCollectionReference.orderBy('timestamp', descending: true)).thenReturn(mockQuery);
when(mockQuery.startAfterDocument(mockDocumentSnapshot)).thenReturn(mockQuery);
when(mockQuery.limit(20)).thenReturn(mockQuery);
when(mockQuery.get()).thenAnswer((_) async => mockQuerySnapshot);
when(mockQuerySnapshot.docs).thenReturn([mockQueryDocumentSnapshot]);
when(mockQueryDocumentSnapshot.data()).thenReturn(message);
//act
final result = await remoteDataSource.loadPreviousMessages(roomID: roomID, lastFetchedMessageID: lastMessageID);
//assert
expect(result, messages);
});
}
@jlaskowska
Copy link

Hi @utkarsh-UK! I read your medium article about Writing Unit Tests in Flutter with Firebase Firestore. I tried to mock the unit test as you suggested but I stumbled across some problems. Is it possible to see a complete example of your code? Or if that is not possible, could you explain what responseMap would be?

Also did you have any issues mocking FirebaseFirestore.instance? I followed the instructions here, however I always receive the error MissingPluginException(No implementation found for method DocumentReference#get on channel plugins.flutter.io/firebase_firestore).

Thanks in advance!

@utkarsh-UK
Copy link
Author

Hi @utkarsh-UK! I read your medium article about Writing Unit Tests in Flutter with Firebase Firestore. I tried to mock the unit test as you suggested but I stumbled across some problems. Is it possible to see a complete example of your code? Or if that is not possible, could you explain what responseMap would be?

Also did you have any issues mocking FirebaseFirestore.instance? I followed the instructions here, however I always receive the error MissingPluginException(No implementation found for method DocumentReference#get on channel plugins.flutter.io/firebase_firestore).

Thanks in advance!

Hi @jlaskowska, thanks for reading my article and kudos to your efforts.

  1. I can't share the full code as of now as I'm bounded legally. But as you asked, the responseMap is just a Map<String, dynamic> map which you are expecting to be returned from fireabse. Here is an example of what I've used relative to my needs:

final responseMap = { "name": "name", "image": url"", "last_read": "", "id": "id" };

Hope this solves your problem!

  1. For your second query, it would be helpful to know if you're mocking Firestore instance for unit tests or widget tests? Please revert back on this with firestore lib version so that I can dig down to right direction.

Thanks!

@jlaskowska
Copy link

Hi @utkarsh-UK! Thanks for your quick reply. I am mocking Firestore instance for unit tests. The version of firebase lib that I am using is firebase_core: ^0.5.1.

@utkarsh-UK
Copy link
Author

I'll write here if I find the solution.

@taiyrbegeyev
Copy link

taiyrbegeyev commented Mar 24, 2021

I have followed all the steps and even added a separate file mock.dart (just copy pasted code from here https://github.com/FirebaseExtended/flutterfire/blob/master/packages/cloud_firestore/cloud_firestore/test/mock.dart) in order to fix problems with Firebase.initializeApp(). I ended up getting the following errors:

MissingPluginException(No implementation found for method DocumentReference#get on channel plugins.flutter.io/firebase_firestore)
  package:cloud_firestore_platform_interface/src/method_channel/utils/exception.dart 13:5                     convertPlatformException
  package:cloud_firestore_platform_interface/src/method_channel/method_channel_document_reference.dart 80:13  MethodChannelDocumentReference.get

@SayuruSandaru
Copy link

Hey, I finally pass the firestore unit test. But again I am stuck in a point where I need to test a function called writeNewUser that returns a void by creating a new document when it called. I tried to test that as follows using verify()

verify(instance.collection(''Name"))
verify(mockCollectionReference.doc(''Name''))

but only the first verify method gets call but the writeNewUser() function executes without any error perfectly as I expected.

@Babwenbiber
Copy link

You may update this for null-safety.
Right now, I get
type 'MockCollectionReference<Object?>' is not a subtype of type 'MockCollectionReference<Map<String, dynamic>>' in type cast
on when(firestore.collection("users")).thenReturn(userCollection);

@Babwenbiber
Copy link

I figured it out how to use your implementation In null-safety and with the code generation of Mockito.

Code gen:

Map<String, bool> getData() {
  return {}; //this will be overriden
}

@GenerateMocks(
  [
    FirebaseFirestore,
    CollectionReference,
    DocumentReference,
    QuerySnapshot,
  ],
  customMocks: [
    MockSpec<QueryDocumentSnapshot>(
        as: #MockQueryDocumentSnapshot, fallbackGenerators: {#data: getData}),
  ],
)

Then in your declaration of your mocked vars you need to tell the compiler what you are actually extending:

late MockFirebaseFirestore firestore;
late MockCollectionReference<Map<String, dynamic>> userCollection;
late MockDocumentReference<Map<String, dynamic>> doc;
late MockCollectionReference<Map<String, dynamic>> notificationCollection;
late MockQuerySnapshot<Map<String, dynamic>> querySnapshot;
late MockQueryDocumentSnapshot<Map<String, dynamic>> queryDocumentSnapshot;
...
setState 
...

Here is a usage of my mocked methods:

when(firestore.collection("users")).thenReturn(userCollection);
when(userCollection.doc("1")).thenReturn(doc);
when(doc.collection("notifications")).thenReturn(notificationCollection);
when(notificationCollection.get()).thenAnswer((_) async => querySnapshot);
when(querySnapshot.docs)
          .thenAnswer((realInvocation) => [queryDocumentSnapshot]);
when(queryDocumentSnapshot.data())
          .thenAnswer((realInvocation) => {"message": true});

To understand why the custom mock is needed read this issue and the readme of the Mockito repo.

Hope that helps anyone.

@sath26
Copy link

sath26 commented Sep 16, 2021

when(instance.collection("sold")).thenReturn(mockCollectionReferenceSold);
    when(mockCollectionReferenceSold.doc("abc"))
        .thenReturn(mockDocumentReferenceSold);
   
    when(mockDocumentReferenceSold.set({
      'appointmentID': "tAppointmentID",
      'date': DateTime.parse("2020-12-05 20:18:04Z"),
      'description': "test description",
    })).thenAnswer((_) async => mockDocumentSnapshotSold);

    print(mockDocumentSnapshotSold.id);

prints null

any idea?
why data hasnt inserted into firestore?

@Babwenbiber
Copy link

If what You write is your whole code, then you are refering to mockDocumentSnapshotSold.id, but didn't call mockDocumentReferenceSold

@sath26
Copy link

sath26 commented Sep 17, 2021

@Babwenbiber could u show in code how and where it shoudl be called? i am not getting you.

@sumanpdneupane
Copy link

I am also stuck in the same as @sath26.
prints null

sath26 commented on Sep 16 •
when(instance.collection("sold")).thenReturn(mockCollectionReferenceSold);
when(mockCollectionReferenceSold.doc("abc"))
.thenReturn(mockDocumentReferenceSold);

when(mockDocumentReferenceSold.set({
  'appointmentID': "tAppointmentID",
  'date': DateTime.parse("2020-12-05 20:18:04Z"),
  'description': "test description",
})).thenAnswer((_) async => mockDocumentSnapshotSold);

print(mockDocumentSnapshotSold.id);

prints null

any idea?
why data hasnt inserted into firestore?

@sumanwebidigital
Copy link

How can we test streams in firestore and model class.
//arrange
when(instance.collection("todos")).thenReturn(mockCollectionReference);
when(mockCollectionReference.doc(any)).thenReturn(mockDocumentReference);
when(mockDocumentReference.collection("todos"))
.thenReturn(mockCollectionReference);
when(mockCollectionReference.snapshots())
.thenReturn(mockQuerySnapshotStream);
when(await mockQuerySnapshotStream.toList())
.thenReturn(mockQuerySnapshotList);
when(mockQuerySnapshotList.map((e) {
final List retVal = [];
for (final DocumentSnapshot doc in e.docs) {
retVal.add(TodoModel.fromDocumentSnapshot(documentSnapshot: doc));
}
return retVal;
}));

  //test
  int i = 0;
  stream.listen(
    expectAsync1((event) {
      expect(event.content, actual[i].content);
      i++;
    }, count: take),
  );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment