32

I am trying to test a method that uses FirebaseFirestore but I am not able to mock the FirebaseFirestore.instance property.

I am following these examples:

  1. Initializing the core: https://firebase.flutter.dev/docs/overview#initializing-flutterfire
  2. Using the firestore plugin: https://firebase.flutter.dev/docs/firestore/usage

I am using the code below for my class and it is working well, which means the firestoreInstance is loaded correctly

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    Main(firebaseApp: Firebase.initializeApp()),
  );
}

class Main extends StatelessWidget {
  final Future<FirebaseApp> firebaseApp;
  const Main({this.firebaseApp});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: firebaseApp,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          final firestoreInstance = FirebaseFirestore.instanceFor(
            app: snapshot.data,
          );
          return MyWidget();
        }
        return CircularProgressIndicator();
      },
    );
  }
}

But when I run the test below I got the message:

"The following FirebaseException was thrown building Builder(dirty): [core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()"


import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

class MockFirebaseApp extends Mock implements FirebaseApp {}

void main() {
  FirebaseApp firebaseApp;

  setUp(() async {
    TestWidgetsFlutterBinding.ensureInitialized();
    firebaseApp = MockFirebaseApp();
  });

  group('Main', () {
    testWidgets('Loads my widget', (WidgetTester tester) async {
      await tester.runAsync(() async {
        await tester.pumpWidget(
          Main(firebaseApp: Future.value(firebaseApp)),
        );

        expect(find.byType(CircularProgressIndicator), findsOneWidget);
      });
    });
  });
}

4
  • 1
    Does this answer your question? No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() in Flutter and Firebase Commented Aug 31, 2020 at 13:52
  • 2
    No, this link shows how to implement the class, I am doing exactly that and it works like a charm. My problem is in the unit-test, because I don't know how to mock the Firebase.initializeApp() method, in my example in the description I am using Mockito to Mock the return of the call that is an FirebaseApp object Commented Aug 31, 2020 at 21:14
  • I'm not sure what you mean by mocking the Firebase.initializeApp() method. While Mockito works great for all purpose mocking, I would like to point out that there also exists Mock Cloud Firestore which can be closer to what you need for Firestore. Mock Clou Firestore in Flutter documentation Commented Sep 2, 2020 at 9:28
  • 2
    What I want with "mocking the Firebase.initializeApp() method" is quite simple. I want to receive a FirebaseApp instance that works when the unit-test reaches the FirebaseFirestore.instanceFor( app: MOCKED_MY_FIREBASEAPP_INSTANCE, ) Indeed Mockito works great mocking the method, but it's returned object does not work in the FirebaseFirestore.instanceFor method I have checked the CloudFirestore Mock project, maybe I can use it, but for that, I will need to do a considerable refactoring in my code. I've tried to use it only to get a FirebaseApp instance, but it also had not worked for me. Commented Sep 4, 2020 at 12:07

4 Answers 4

41

I had the same problem. Using this answer I found the solution.

  1. Copy the contents of: https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_auth/firebase_auth/test/mock.dart into a file, that you can import into your tests, where you need to initialize a Firebase app.

  2. Call setupFirebaseAuthMocks(); at the top of your main function for all your tests.

  3. In your setUpAll function, call await Firebase.initializeApp(); (You can also put this in your main function under setupFirebaseAuthMocks(); will still work).

Now you should have a mocked Firebase app.

Here is a full example:

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_test/flutter_test.dart';
import '../lib/authentication/your_auth_using_firebase.dart';
import './mock.dart'; // from: https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_auth/firebase_auth/test/mock.dart



void main() {
  // TestWidgetsFlutterBinding.ensureInitialized(); Gets called in setupFirebaseAuthMocks()
  setupFirebaseAuthMocks();

  setUpAll(() async {
    await Firebase.initializeApp();
  });

  testWidgets('Your Test', (WidgetTester tester) async {
    final YourFirebaseAuthClass authService = YourFirebaseAuthClass();
    // Tests to write
  });
}
Sign up to request clarification or add additional context in comments.

7 Comments

Thank you. Not the cleaner way to test it but it works for me too.
Gareth - great callout but doesn't seem to work with firebase crashlytics in the app, or at least i'm getting assertion errors there
thanks this does give me a good start but also firebase messaging throws MissingPluginException(No implementation found for method Messaging#startBackgroundIsolate on channel plugins.flutter.io/firebase_messaging)
Great Start, thank you. Is there any way to cleanly import the mocks?
thank you very much friend. I spent about 4 hours trying various implementations. this was what i needed.
|
4

TO mock Firebase app you can use the following method from firerbase_core_plarform_interface plugin.

import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

typedef Callback = void Function(MethodCall call);

void setupFirebaseAuthMocks([Callback? customHandlers]) {
  TestWidgetsFlutterBinding.ensureInitialized();

  setupFirebaseCoreMocks();
}

Future<T> neverEndingFuture<T>() async {
  while (true) {
    await Future.delayed(const Duration(minutes: 5));
  }
}

5 Comments

why is this not the top/accepted answer ?
@Malbolged because it doesn't have much explanation.
@anasqadrei doesn't matter when this is THE correct answer ...
@Malbolged yes, it matters. Check the guides here at SO on how to write good answers. This answer, although is correct, doesn't provide clear explanation
And in the end of the day it's the only correct answer ... Sure adding clear explanation would be helpful, and would follow the guidelines but when the top answer is wrong/bad/outdated then guidelines are not important.
4

Maybe the fake_cloud_firestore package is useful for you:

Fakes to write unit tests for Cloud Firestore. Instantiate a MockFirestoreInstance, then pass it around your project as if it were a FirestoreInstance. This fake acts like Firestore except it will only keep the state in memory. To help debug, you can use MockFirestoreInstance.dump() to see what's in the fake database. This is useful to set up the state of your database, then check that your UI behaves the way you expect.

Example from the docs:

import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';

void main() {
  final instance = FakeFirebaseFirestore();
  await instance.collection('users').add({
    'username': 'Bob',
  });
  final snapshot = await instance.collection('users').get();
  print(snapshot.docs.length); // 1
  print(snapshot.docs.first.get('username')); // 'Bob'
  print(instance.dump());
}

In a UI test you would (example from the docs):

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
import 'package:firestore_example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

const MessagesCollection = 'messages';
    
        

void main() {
  testWidgets('shows messages', (WidgetTester tester) async {
    // Populate the fake database.
    final firestore = FakeFirebaseFirestore();
    await firestore.collection(MessagesCollection).add({
      'message': 'Hello world!',
      'created_at': FieldValue.serverTimestamp(),
    });

    // Render the widget.
    await tester.pumpWidget(MaterialApp(
        title: 'Firestore Example', home: MyHomePage(firestore: firestore)));
    // Let the snapshots stream fire a snapshot.
    await tester.idle();
    // Re-render.
    await tester.pump();
    // // Verify the output.
    expect(find.text('Hello world!'), findsOneWidget);
    expect(find.text('Message 1 of 1'), findsOneWidget);
  });
}

EDIT: For those combing from cloud_firestore_mocks the author renamed this package to fake_cloud_firestore.

DI: You might also want to have a look at get_it, which allows you to combine things with dependency injection. This is helpful for component tests in which you want to instantiate a service that depends on the mock Firestore. Make everything you need to mock out a constructor parameter and inject it using get_it. In the tests you can then inject the fake_cloud_firestore mocks if you want to instantiate such a service.

Comments

0

think that is for use FutureBuilder as first Widget o root widget of the tree, flutter needs always a MaterialApp or others(dont remmember right now).

Recomendation, refactor FutureBuilder -> Wrap whit widget -> MaterialApp(home:FutureBuilder(...

and check MyWidget() first widget, maybe same MaterialApp get error, but not sure.

if you are using routes method to navigate use initialRoute.

Luck!

1 Comment

I can do this, but the problem will be the same, my problem is trying to mock the return of the Firebase.initializeApp() method, which means, I need to mock a FirebaseApp object that works in FirebaseFirestore.instanceFor( app: MY_MOCKED_OBJECT, )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.