Flutter单元测试中如何使用Mockito模拟Firebase Crashlytics?
Flutter单元测试模拟Firebase Crashlytics实现方案
问题场景
接入Firebase Crashlytics后,对下述异常处理逻辑编写单元测试时:
if (queryResult.hasException) { String msg = queryResult.exception.toString(); await FirebaseCrashlytics.instance.recordError(error, stackTrace, reason: message, fatal: true); throw Exception("Query execution failed due to: " + msg); }
如果不对Firebase实例做模拟,测试运行时会直接抛出报错:
No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
需要在测试中模拟Firebase实例,拦截真实的错误记录操作,既避免初始化报错,也防止测试数据上报到线上Crashlytics后台。
具体实现
- 先添加测试需要的依赖
在pubspec.yaml的dev_dependencies节点下加入以下依赖:dev_dependencies: flutter_test: sdk: flutter mockito: ^5.4.0 build_runner: ^2.4.0 firebase_core_platform_interface: ^4.8.0 firebase_crashlytics_platform_interface: ^3.6.0 - 测试初始化阶段注入Mock实例,跳过真实SDK初始化
不需要在测试里真的调用Firebase.initializeApp()连接线上服务,直接利用Firebase提供的测试桩替换底层平台实现即可:import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_crashlytics_platform_interface/firebase_crashlytics_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; class MockCrashlyticsPlatform extends Mock implements FirebaseCrashlyticsPlatform {} void main() { late MockCrashlyticsPlatform mockPlatform; setUpAll(() async { TestWidgetsFlutterBinding.ensureInitialized(); // 注入Firebase Core测试桩,绕过DEFAULT应用初始化校验 setupFirebaseCoreMocks(); // 生成测试用的空Firebase实例,不会发起真实网络请求 await Firebase.initializeApp(); // 替换Crashlytics底层平台实例为Mock对象 mockPlatform = MockCrashlyticsPlatform(); FirebaseCrashlyticsPlatform.instance = mockPlatform; // 给recordError设置默认空返回,避免调用时抛未实现异常 when(mockPlatform.recordError(any, any, any)).thenAnswer((_) async {}); }); - 编写测试用例验证逻辑
测试过程中既可以校验异常是否正常抛出,也可以验证Crashlytics的上报逻辑是否按预期触发,全程不会产生真实上报:test('查询出现异常时会调用Crashlytics记录错误并抛出包装后的异常', () async { // 构造带异常的测试查询结果 final testQueryResult = QueryResult( hasException: true, exception: OperationException(), source: null, ); // 验证方法确实抛出了预期异常 expect( () => runYourQueryLogic(testQueryResult), throwsA(isA<Exception>().having( (e) => e.toString(), '异常信息', contains('Query execution failed due to'), )), ); // 验证Crashlytics的recordError确实被调用了1次 verify(mockPlatform.recordError(any, any, any)).called(1); }); }
注意事项
- 不要在单元测试中真实初始化线上Firebase应用,否则测试过程中产生的错误记录会上传到线上Crashlytics后台,污染真实的崩溃数据。
- 如果觉得手写Mock类麻烦,可以给
FirebaseCrashlyticsPlatform添加@GenerateMocks注解,通过build_runner命令自动生成Mock代码,减少手动编码工作量。 - 业务代码中原有的
FirebaseCrashlytics.instance调用不需要做任何修改,Firebase SDK本身做了平台层解耦,替换底层平台实例后,上层所有调用都会自动路由到Mock对象上,不会触发真实的上报逻辑。
内容的提问来源于stack exchange,提问作者Răzvan Puiu
相关产品推荐
相关产品推荐

