Flutter Firebase测试问题:initializeApp()卡住,移除则触发初始化错误
问题:测试依赖Firestore的AdminVerificationScreen组件时,Firebase.initializeApp()卡住或报错
我正在测试一个依赖Firestore Repository的AdminVerificationScreen组件,编写的测试代码如下:
void main() { group('AdminVerificationScreen', () { testWidgets('renders correctly', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); //pump the widget using a bloc provider and a multirepository provider await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); await tester.pumpWidget( MultiRepositoryProvider( providers: [ RepositoryProvider( create: (_) => FirestoreRepository(), ), RepositoryProvider( create: (context) => TypesenseRepository(), ), ], child: MultiBlocProvider( providers: [ BlocProvider<AdminVerificationBloc>( create: (context) => AdminVerificationBloc( databaseRepository: context.read<FirestoreRepository>(), typesenseRepository: context.read<TypesenseRepository>()), ), ], child: MaterialApp( home: AdminVerificationScreen(), ), ), ), ); //expect the widget to be found expect(find.byType(AdminVerificationScreen), findsOneWidget); }); }); }
现在遇到的问题是:调用Firebase.initializeApp()时会卡住无法继续执行,但移除该调用则会出现错误:
[core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
请问我哪里操作出错了?
解决方案
你碰到的问题本质是在Widget测试中直接初始化真实的Firebase实例导致的阻塞——真实Firebase初始化需要网络连接、权限等环境支持,测试环境里很难满足这些条件,所以会卡住;但如果不初始化,依赖Firebase的代码又会报错。
正确的做法是在测试中Mock(模拟)Firestore Repository和Firebase相关依赖,而不是使用真实的Firebase服务,这样既不用初始化真实Firebase,也能测试组件的UI渲染逻辑。
具体可以这么改:
- 首先,确保你的
FirestoreRepository是基于抽象类实现的(比如DatabaseRepository),这样测试时可以替换成Mock实现。如果还没做抽象,先重构:
// 定义抽象仓库 abstract class DatabaseRepository { // 这里放仓库的方法签名,比如getAdminData()等 } // 真实Firestore实现 class FirestoreRepository implements DatabaseRepository { // 真实Firestore逻辑 }
- 创建Mock仓库类,用
mockito包来生成(或者手动写简单实现):
// 使用mockito的例子 class MockDatabaseRepository extends Mock implements DatabaseRepository {} class MockTypesenseRepository extends Mock implements TypesenseRepository {}
- 修改测试代码,用Mock仓库替代真实实现,并且不需要初始化Firebase:
void main() { group('AdminVerificationScreen', () { late MockDatabaseRepository mockDbRepo; late MockTypesenseRepository mockTypesenseRepo; setUp(() { // 初始化Mock实例 mockDbRepo = MockDatabaseRepository(); mockTypesenseRepo = MockTypesenseRepository(); WidgetsFlutterBinding.ensureInitialized(); }); testWidgets('renders correctly', (WidgetTester tester) async { await tester.pumpWidget( MultiRepositoryProvider( providers: [ RepositoryProvider<DatabaseRepository>( create: (_) => mockDbRepo, ), RepositoryProvider<TypesenseRepository>( create: (_) => mockTypesenseRepo, ), ], child: MultiBlocProvider( providers: [ BlocProvider<AdminVerificationBloc>( create: (context) => AdminVerificationBloc( databaseRepository: context.read<DatabaseRepository>(), typesenseRepository: context.read<TypesenseRepository>()), ), ], child: MaterialApp( home: AdminVerificationScreen(), ), ), ), ); // 等待UI渲染完成 await tester.pumpAndSettle(); //expect the widget to be found expect(find.byType(AdminVerificationScreen), findsOneWidget); }); }); }
为什么这个方法可行?
- 我们用Mock仓库替代了真实的Firestore依赖,测试不再需要连接真实Firebase服务,也就不用调用
Firebase.initializeApp() - 这种方式专注于测试组件的UI渲染和交互逻辑,而不是测试Firestore本身的功能(Firestore的功能应该单独写集成测试)
- 如果你的Bloc有依赖仓库的逻辑,可以通过Mock来预设返回值,测试不同状态下的UI表现
另外,如果你一定要在测试中使用真实Firebase(比如集成测试),可以考虑使用firebase_core的测试初始化方式,或者使用Firebase Emulator,但这会增加测试的复杂度,一般Widget测试不推荐这么做。
内容的提问来源于stack exchange,提问作者Ruder Buster
相关产品推荐
相关产品推荐

