firestore_fakes

This library gives you a set of fakes that allow you fake Firebase Firestore. It is similar to fake_cloud_firestore in that you can fake Firestore. However, this library gives you more control. You can add code to capture the events where documents are added to a collection, and you can capture the events where there is a fetch request. You can return whatever you want, or let the fakes automatically manage the state for you.

Stateful vs. Stateless

You configure the classes with function composition by passing values into their constructors. You can manage the state (or use no state) external to the classes, or you can allow the classes to automatically manage the state internally. When the fakes manage the state internally, they aim to mimic the functionality of Firestore. It is possible to write an entire app that stores data in memory and behaves like the real Firestore database.

This is a stateful example that mimics streaming. You can use this as your Flutter state management solution. Check out the example tab for a Flutter example that updates the state using StreamBuilder.

//This mimics Firestore
final firestore = FirebaseFirestoreFake.stateful();

//Grab the stream of book document snapshots
final snapshots = firestore
    .collection('books')
    .where('category', isEqualTo: 'philosophy')
    .snapshots();

//Awaits the first snapshot which will have no data
await snapshots.first;

final secondSnapshotFuture = snapshots.first;

//Add a book document
await firestore.collection('books').add({
  'title': 'The Republic',
  'category': 'philosophy',
});

//Await the new book to be inserted
final secondQuerySnapshot = await secondSnapshotFuture;

print(secondQuerySnapshot.docs[0].data()['title']);

Here is a stateless example. This requires more configuration but gives you absolute control.

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firestore_fakes/collection_reference_fake.dart';
import 'package:firestore_fakes/document_reference_fake.dart';
import 'package:firestore_fakes/document_snapshot_fake.dart';
import 'package:firestore_fakes/firebase_firestore_fake.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:uuid/uuid.dart';

void main() {
  test('Test adding a user to the users collection and then retrieving it',
      () async {
    //Arrange: Configure the fake Firestore
    FirebaseFirestore firestore = setup();

    //Act: call the actual code
    final actualDocumentSnapshot = await addAndFetchUser(
      firestore,
      <String, dynamic>{
        "first": "Ada",
        "last": "Lovelace",
        "born": 1815,
      },
    );

    //Assert: Check the results
    expect(actualDocumentSnapshot.data()!['born'], 1815);
    expect(actualDocumentSnapshot.data()!['first'], 'Ada');
    expect(actualDocumentSnapshot.data()!['last'], "Lovelace");
  });
}

///Arrange: Configure the fake
FirebaseFirestoreFake setup() {
  //The users collection
  final users = <String, DocumentReferenceFake>{};

  var usersCollectionReference = CollectionReferenceFake(
    addDocumentReference: (data) async {
      //Generate a random documentId
      final documentId = const Uuid().v4();

      //Put the document in the users map by id and return it
      return users.putIfAbsent(
        documentId,
        () => DocumentReferenceFake(
            () async => DocumentSnaphotFake(data), documentId),
      );
    },
    documentReference: (path) => users[path]!,
  );

  //Declare the map of collections
  final collections = {'users': usersCollectionReference};

  //Return the fake firestore
  return FirebaseFirestoreFake((name) => collections[name]!);
}

///Act: This is the real code that you might find in your app
Future<DocumentSnapshot<Map<String, dynamic>>> addAndFetchUser(
  FirebaseFirestore firestore,
  Map<String, dynamic> user,
) async {
  //Add user to users collection
  final documentReference = await firestore.collection('users').add(user);

  //Get document by Id
  final fetchedDocumentReference =
      firestore.collection('users').doc(documentReference.id);

  //Return the document
  return fetchedDocumentReference.get();
}

Please understand that this is only an alpha release and only the bare minimum was implemented. Feel free to add PRs to implement more functionality.