How to test a Flutter application using Firebase? - firebase

I am using Flutter and Firebase as my database. I want to do some unit test of my application, but when I start this test :
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
// Create the widget by telling the tester to build it.
await tester.pumpWidget(LoginPage());
expect(true, true);
});
I have the Exception :
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
But Firebase.initializeApp() is called in my main.dart here :
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
And I don't know how to initialize the Firebase app in my test.

Posting this as a community wiki as it's based on #GuilhermeGabanelli's comment.
If you check this flutterfire documentation on how to perform unit tests with Firebase Services you will see that:
The Firebase libraries need to run on an actual device or emulator. So if you want to run unit tests, you'll have to use Fakes instead. A Fake is a library that implements the API of a given Firebase library and simulates its behavior.
...
When initializing your app, instead of passing the actual instance of a Firebase library (e.g. FirebaseFirestore.instance if using Firestore), you pass an instance of a fake (e.g. FakeFirebaseFirestore()). Then the rest of your application will run as if it were talking to Firebase.
Followed by an example with sample code. I believe this is the cause of the issue you are facing and if you change your code to use Fakes this should be fixed.

Related

Is it possible to assert the number of reads/writes in a Firebase Cloud Function test?

I have a test suite that runs integration tests for my Firebase Cloud Functions against a locally running Firebase emulator, is it possible to assert the number of reads/writes made to the emulated Firestore instance?
E.g. for the following function I would want to assert that 1 write occurs & 0 reads
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.addMessage = functions.https.onCall(async (data, context) => {
const original = data.query.text;
const snapshot = await admin.firestore().collection("/messages").add({ original: original });
return { code: 200, message: "Success" };
});
Currently, Its not possible to assert the number of reads and writes in a Firebase Cloud Function test.
I would suggest that the best way would be is to write the test data to the Firebase Emulator, then read it back and also count the number of collections/documents as well using Client SDK.
If you want to assert data coming from the Firebase Emulator, then you can use Requests Monitor. We used Client SDK for this purpose as Admin SDK requests and access calls will not be listed because it bypass Security Rules. It's a current limitation according to this Blog.
The Firebase emulator has a log that shows requests as shown in example below.
Note: I don't think that you should have a dependency on it, since Firebase Emulator Request Monitor is a new feature that its implementation may change and Admin SDK can be included over time.

Can't create unit test with FireStore for flutterfire, throws during init, even with simplest possible test

// ignore_for_file: avoid_print
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('core tests - coffee', () {
test('fetch coffees', () async {
await Firebase.initializeApp();
final FirebaseFirestore firestore = FirebaseFirestore.instance;
print('initialized');
expect(firestore, isNotNull);
});
});
}
This is in a test. Oddly, it works in the live app, but not in a simple test case.
Errors during Firebase.initializeApp() with:
Null check operator used on a null value
MethodChannel.binaryMessenger
package:flutter/…/services/platform_channel.dart:121
I don't want a testWidgets. I don't want a mocked FireStore. I want to hit the real database.
I'm on a mac, running a simulator, using VSC, although the simulator shouldn't even be involved.
Firebase plugins wraps around native platform (firebase) SDKs and i think they are not available on when running tests.
I would suggest to use either the emulator suite or fake_cloud_firestore package, or do manual testing by humans (connected with a different firebase instance)

How do the rest of the modules access "Firebase" class, after we initialize using ```Firebase.initializeApp();``` in the entry point?

So my app is working, but I want to understand what does the initialization actually do?
and why do we have access to a "Firebase" class only after we initialize?
in other words:
How do the rest of the modules access "Firebase" class, after we initialize using Firebase.initializeApp(); in the entry point?
for reference, this is one approach of how we initialize:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp()
);
}
My question is, how is the returned value of the initialized app stored? so that I can access it from any module?
In your case (no application name provided), it initializes a new FirebaseAppPlatform for the default Firebase App and returns it as a Future<FirebaseAppPlatform>. This method should be called before any usage of FlutterFire plugins.
Thanks to that FirebaseApp instance, all the Firebase module can get access to the initialized Firebase Application. Example for Firestore: FirebaseFirestore.instance
Thanks to that initialization using await, you'll also get a synchronous getter for the currentUser.

Firebase functions logging does not work from non-callable functions

I'm getting started with Firebase Functions. I made a simple callable function:
exports.myCallable = functions.https.onCall((data, context) => {
console.log('I am here.');
functions.logger.log('I am here 2.');
});
As expected, I see these logs in the Logs tab in Firebase Console.
However, I also made this function, which should trigger when the user creates their account.
exports.setGroupAfterAuth = functions.auth.user().onCreate((user) => {
console.log('I am here 3.');
functions.logger.log('I am here 4.');
});
In this case, I see that the function was called in Firebase Console, but I do not see my logs.
Why do the logs appear in Firebase Console for the first function but not the second, and what can I do to make them appear in the second?
It's not possible to use the Firebase Functions client SDK to invoke an auth onCreate function.
Auth onCreate functions can only be invoked by actually creating a new user account. They cannot be invoked directly from your app.
The client SDK can only be used to invoke callable functions, as described in the linked documentation. The funciton must be declared with functions.https.onCall.

How to crash Flutter App for Android and iOS (to test Firebase Crashlytics)?

I want to test out crash report using Firebase Crashlytics in my Flutter App. I need to Fatal crash my flutter app for both Android and iOS programmatically. Any idea?
If you have the flutter_crashlytics plugin, you can test it using
FirebaseCrashlytics.instance.crash();
You should be able to throw a Dart exception by doing this anywhere in your Flutter app:
throw Exception("This is a crash!");
Or by using an arbitrary object:
throw "This is a crash!";
You can find more info about dart exceptions in the language tour and if you want, you can create your own custom Exception type as explained in this SO answer
integrate firebase_crashlytics
Crashlytics.instance.crash();
using firebase_crashlytics plugin
FirebaseCrashlytics.instance.crash();
You should enable crashlytics
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
if (kDebugMode) {
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);//disable false
}else{
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
You can crash you Flutter app by many ways:
throw(Exception('Hello Crashlytics')); like in #Xavi Rigau's answer
List list;
list.add("add");
Like #Paresh Mangukiya does.
3.
final a = [12];
print(a[1]);
But there is 2 IMPORTANT thing you should consider:
You should run your app in release mode, for example with the command flutter run --release
In Crashlytycs console, by default shown only fatal crashes. The crashes above are non-fatal crashes. So, you should clear the filter to see all crash types. (Filter can be filtered in crashlytics dashboard by clicking (x) button near filter). More on this you can read from this SO answer
Check this out . This is from the official documentation. https://firebase.flutter.dev/docs/crashlytics/usage
Once your configuration is proper according to documentation for crashlytics, FirebaseCrashlytics.instance.crash(); will crash the app.

Resources