'Flutter Test: temporarily disable Firebase or somehow make the test not fail because of Firebase?
I wrote a test:
homeScreenWidgetsTest() {
testWidgets('Home page screen widgets', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home:Provider<AppDatabase>(
create: (context) => AppDatabase(),
child: HomePage(),
dispose: (context, db) => db.close(),
),
),
);
await tester.pumpAndSettle(Duration(seconds: 15));
expect(find.byType(AppBar), findsOneWidget);
});
}
It throws this error:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following FirebaseException was thrown running a test:
[core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
When the exception was thrown, this was the stack:
#0 MethodChannelFirebase.app (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:149:5)
#1 Firebase.app (package:firebase_core/src/firebase.dart:55:41)
#2 FirebaseCrashlytics.instance (package:firebase_crashlytics/src/firebase_crashlytics.dart:33:55)
#3 FirebaseLogging.log (package:.../logging/firebase_logging.dart:5:25)
#4 MyClass.work (package:.../oauth/oauth.dart:136:21)
<asynchronous suspension>
The error happens at the log line:
static Future<bool> work() async {
FirebaseLogging.log("Start work.");
If I add the Firebase.initializeApp()
in the main method of my test:
Future<void> main() async {
await Firebase.initializeApp();
homeScreenWidgetsTest();
}
It throws these errors:
package:flutter/src/services/platform_channel.dart 142:86 MethodChannel.binaryMessenger
package:flutter/src/services/platform_channel.dart 148:36 MethodChannel._invokeMethod
package:flutter/src/services/platform_channel.dart 331:12 MethodChannel.invokeMethod
package:flutter/src/services/platform_channel.dart 344:41 MethodChannel.invokeListMethod
package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart 31:37 MethodChannelFirebase._initializeCore
package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart 73:13 MethodChannelFirebase.initializeApp
package:firebase_core/src/firebase.dart 42:47 Firebase.initializeApp
test\home_screen_test.dart 14:18 main
Failed to load "E:\...\home_screen_test.dart": Null check operator used on a null value
So it throws error at this line: await Firebase.initializeApp();
How to fix this problem? Thanks in advance.
This is the FirebaseLogging.log():
class FirebaseLogging {
static void log(String log) {
FirebaseCrashlytics.instance.log(log);
}
}
Solution 1:[1]
I was having the same issue when I updated the Firebase package.
I created a class to swap FirebaseAnalytics
with a mocked version based on the environment. Then replaced FirebaseAnalytics
with the wrapper class AnalyticsService
.
This is so hacky, and I hate it.
If anyone has a better way, please share.
import 'dart:io';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_core/firebase_core.dart';
// The is class is needed because firebase is horrible and apparently hates developers
// - also calling `FirebaseAnalytics.instance` causes tests to fail
class AnalyticsService {
static get instance => Platform.environment.containsKey('FLUTTER_TEST') ? FakeFirebaseAnalytics() : FirebaseAnalytics.instance;
}
class FakeFirebaseAnalytics implements FirebaseAnalytics {
@override
FirebaseApp app;
@override
FirebaseAnalyticsAndroid get android => null;
@override
Future<void> logAdImpression({String adPlatform, String adSource, String adFormat, String adUnitName, double value, String currency, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logAddPaymentInfo({String coupon, String currency, String paymentType, double value, List<AnalyticsEventItem> items, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logAddShippingInfo({String coupon, String currency, double value, String shippingTier, List<AnalyticsEventItem> items, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logAddToCart({List<AnalyticsEventItem> items, double value, String currency, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logAddToWishlist({List<AnalyticsEventItem> items, double value, String currency, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logAppOpen({AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logBeginCheckout({double value, String currency, List<AnalyticsEventItem> items, String coupon, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logCampaignDetails({String source, String medium, String campaign, String term, String content, String aclid, String cp1, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logEarnVirtualCurrency({String virtualCurrencyName, num value, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logEcommercePurchase(
{String currency,
double value,
String transactionId,
double tax,
double shipping,
String coupon,
String location,
int numberOfNights,
int numberOfRooms,
int numberOfPassengers,
String origin,
String destination,
String startDate,
String endDate,
String travelClass}) async {}
@override
Future<void> logEvent({String name, Map<String, Object> parameters, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logGenerateLead({String currency, double value, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logJoinGroup({String groupId, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logLevelEnd({String levelName, int success, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logLevelStart({String levelName, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logLevelUp({int level, String character, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logLogin({String loginMethod, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logPostScore({int score, int level, String character, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logPresentOffer({String itemId, String itemName, String itemCategory, int quantity, double price, double value, String currency, String itemLocationId}) async {}
@override
Future<void> logPurchase(
{String currency, String coupon, double value, List<AnalyticsEventItem> items, double tax, double shipping, String transactionId, String affiliation, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logPurchaseRefund({String currency, double value, String transactionId}) async {}
@override
Future<void> logRefund({String currency, String coupon, double value, double tax, double shipping, String transactionId, String affiliation, List<AnalyticsEventItem> items}) async {}
@override
Future<void> logRemoveFromCart({String currency, double value, List<AnalyticsEventItem> items, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logScreenView({String screenClass, String screenName, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logSearch(
{String searchTerm,
int numberOfNights,
int numberOfRooms,
int numberOfPassengers,
String origin,
String destination,
String startDate,
String endDate,
String travelClass,
AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logSelectContent({String contentType, String itemId}) async {}
@override
Future<void> logSelectItem({String itemListId, String itemListName, List<AnalyticsEventItem> items, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logSelectPromotion(
{String creativeName, String creativeSlot, List<AnalyticsEventItem> items, String locationId, String promotionId, String promotionName, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logSetCheckoutOption({int checkoutStep, String checkoutOption}) async {}
@override
Future<void> logShare({String contentType, String itemId, String method}) async {}
@override
Future<void> logSignUp({String signUpMethod}) async {}
@override
Future<void> logSpendVirtualCurrency({String itemName, String virtualCurrencyName, num value}) async {}
@override
Future<void> logTutorialBegin() async {}
@override
Future<void> logTutorialComplete() async {}
@override
Future<void> logUnlockAchievement({String id}) async {}
@override
Future<void> logViewCart({String currency, double value, List<AnalyticsEventItem> items, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> logViewItem({String currency, double value, List<AnalyticsEventItem> items}) async {}
@override
Future<void> logViewItemList({List<AnalyticsEventItem> items, String itemListId, String itemListName}) async {}
@override
Future<void> logViewPromotion({String creativeName, String creativeSlot, List<AnalyticsEventItem> items, String locationId, String promotionId, String promotionName}) async {}
@override
Future<void> logViewSearchResults({String searchTerm}) async {}
@override
Future<void> resetAnalyticsData() async {}
@override
Future<void> setAnalyticsCollectionEnabled(bool enabled) async {}
@override
Future<void> setConsent({bool adStorageConsentGranted, bool analyticsStorageConsentGranted}) async {}
@override
Future<void> setCurrentScreen({String screenName, String screenClassOverride = 'Flutter', AnalyticsCallOptions callOptions}) async {}
@override
Future<void> setDefaultEventParameters(Map<String, Object> defaultParameters) async {}
@override
Future<void> setSessionTimeoutDuration(Duration timeout) async {}
@override
Future<void> setUserId({String id, AnalyticsCallOptions callOptions}) async {}
@override
Future<void> setUserProperty({String name, String value, AnalyticsCallOptions callOptions}) async {}
@override
Map get pluginConstants => {};
}
To apply this hack to the code in the question:
class FirebaseLogging {
static void log(String log) {
if (Platform.environment.containsKey('FLUTTER_TEST')) return;
FirebaseCrashlytics.instance.log(log);
}
}
Solution 2:[2]
You can mock channels like it's done in official flutter fire. Call initFirebaseForTest will create a fake firebase instance and so firebase analytics won't bother you anymore.
Future<FirebaseApp> initFirebaseForTest([int? counter]) async {
try {
setupFirebaseAuthMocks();
return await Firebase.initializeApp(
name: '$counter ?? 0AB',
options: const FirebaseOptions(
apiKey: '',
appId: '',
messagingSenderId: '',
projectId: '',
),
);
} catch (_) {}
return Firebase.app();
}
void setupFirebaseAuthMocks([Callback? customHandlers]) {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelFirebase.channel.setMockMethodCallHandler((call) async {
if (call.method == 'Firebase#initializeCore') {
return [
{
'name': defaultFirebaseAppName,
'options': {
'apiKey': '123',
'appId': '123',
'messagingSenderId': '123',
'projectId': '123',
},
'pluginConstants': {},
}
];
}
if (call.method == 'Firebase#initializeApp') {
return {
'name': call.arguments['appName'],
'options': call.arguments['options'],
'pluginConstants': {},
};
}
if (customHandlers != null) {
customHandlers(call);
}
return null;
});
}
Future<T> neverEndingFuture<T>() async {
// ignore: literal_only_boolean_expressions
while (true) {
await Future.delayed(const Duration(minutes: 5));
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | mcfly |