如何在Flutter中模拟FirebaseApp

24
我正在尝试测试一个使用FirebaseFirestore的方法,但我无法模拟FirebaseFirestore.instance属性。我正在遵循这些示例:
  1. 初始化核心:https://firebase.flutter.dev/docs/overview#initializing-flutterfire
  2. 使用firestore插件:https://firebase.flutter.dev/docs/firestore/usage
我正在使用下面的代码来编写我的类,并且它运行良好,这意味着firestoreInstance已经正确加载。
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();
      },
    );
  }
}

但是当我运行下面的测试时,我收到了以下消息:

"构建Builder(dirty)时抛出了以下FirebaseException: [core/no-app] 没有创建Firebase App '[DEFAULT]' - 请调用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);
      });
    });
  });
}


1
不,这个链接展示了如何实现类,我正在完全按照那样做,并且它运行得非常好。我的问题在于单元测试中,因为我不知道如何模拟Firebase.initializeApp()方法,在我的描述示例中,我使用Mockito来模拟调用的返回值,即FirebaseApp对象。 - Edson Dias
我不确定您所说的模拟_Firebase.initializeApp()方法的意思。虽然_Mockito_非常适用于所有目的的模拟,但我想指出还存在Mock Cloud Firestore_,它可能更接近于您对Firestore的需求。Flutter文档中的Mock Cloud Firestore - sllopis
1
我想要“模拟Firebase.initializeApp()方法”的目的非常简单。 我希望在单元测试到达FirebaseFirestore.instanceFor(app:MOCKED_MY_FIREBASEAPP_INSTANCE,)时能够接收一个可用的FirebaseApp实例。确实,Mockito在模拟该方法方面表现得非常出色,但是它返回的对象无法在FirebaseFirestore.instanceFor方法中使用。我已经检查了CloudFirestore Mock项目,也许我可以使用它,但是为此,我需要对我的代码进行相当大的重构。我尝试过仅使用它来获取FirebaseApp实例,但是它对我也没有起作用。 - Edson Dias
4个回答

35

我曾经遇到了同样的问题。使用这个答案,我找到了解决方案。

  1. https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_auth/firebase_auth/test/mock.dart的内容复制到一个文件中,并导入到你需要初始化Firebase应用程序的测试中。

  2. 在所有测试的main函数顶部调用setupFirebaseAuthMocks();

  3. 在你的setUpAll函数中,调用await Firebase.initializeApp();(你也可以将其放在setupFirebaseAuthMocks();下的main函数中仍然有效)。

现在你应该有了一个模拟的Firebase应用程序。

以下是一个完整的示例:

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
  });
}

这对我很有效。谢谢 Gareth! - William Terrill
谢谢。虽然不是最好的测试方式,但对我也有效。 - Adrien Arcuri
Gareth - 很好的点名,但似乎不适用于应用中的 Firebase Crashlytics,或者至少我在那里收到了断言错误。 - nAndroid
2
谢谢,这确实给了我一个好的开始,但 Firebase Messaging 也会抛出 MissingPluginException(在通道 plugins.flutter.io/firebase_messaging 上找不到方法 Messaging#startBackgroundIsolate 的实现)。 - martinseal1987
非常感谢你,朋友。我花了大约4个小时尝试各种实现方式,这正是我所需要的。 - Felipe Sales
显示剩余3条评论

3
也许 cloud_firestore_mocks 包对您有用:

用于编写 Cloud Firestore 单元测试的伪造者。实例化一个 MockFirestoreInstance,然后将其传递到您的项目中,就像它是一个 FirestoreInstance 一样。这个伪造者表现得像 Firestore,但它只会保留内存中的状态。为了帮助调试,您可以使用 MockFirestoreInstance.dump() 来查看虚假数据库中的内容。使用这个包有助于设置数据库状态,然后检查您的 UI 是否按照预期工作。

来自文档的示例:

import 'package:cloud_firestore_mocks/cloud_firestore_mocks.dart';

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

2

要模拟 Firebase 应用程序,您可以使用来自 firerbase_core_plarform_interface 插件的以下方法。

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));
  }
}

为什么这不是最佳/被接受的答案? - undefined

0

认为将FutureBuilder用作树的第一个Widget,Flutter始终需要一个MaterialApp或其他(现在记不清了)。

建议重构FutureBuilder -> 用widget包装 -> MaterialApp(home:FutureBuilder(...)

并检查MyWidget()第一个widget,可能会出现相同的MaterialApp错误,但不确定。

如果您正在使用路由方法进行导航,请使用initialRoute。

祝好运!


1
我可以做到这一点,但问题仍然存在,我的问题在于尝试模拟Firebase.initializeApp()方法的返回值,这意味着我需要模拟一个FirebaseApp对象,该对象可在FirebaseFirestore.instanceFor( app: MY_MOCKED_OBJECT, )中正常工作。 - Edson Dias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接