Flutter自定义抽屉与退出按钮测试遇Hive Box打开异常求助
问题:Flutter测试自定义抽屉/退出按钮时出现Hive Box打开异常
我正在为Flutter应用编写自定义抽屉和退出按钮的测试用例,测试代码如下:
import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:talawa/constants/custom_theme.dart'; import 'package:talawa/models/mainscreen_navigation_args.dart'; import 'package:talawa/services/graphql_config.dart'; import 'package:talawa/services/navigation_service.dart'; import 'package:talawa/services/size_config.dart'; import 'package:talawa/utils/app_localization.dart'; import 'package:talawa/view_model/main_screen_view_model.dart'; import 'package:talawa/views/main_screen.dart'; import 'package:talawa/widgets/custom_alert_dialog.dart'; import '../../helpers/test_helpers.dart'; import '../../helpers/test_helpers.mocks.dart'; import '../../helpers/test_locator.dart'; class MockBuildContext extends Mock implements BuildContext {} Widget createHomePageScreen() { return MaterialApp( locale: const Locale('en'), localizationsDelegates: [ const AppLocalizationsDelegate(isTest: true), GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], themeMode: ThemeMode.light, theme: TalawaTheme.lightTheme, home: MainScreen( key: const Key('MainScreen'), mainScreenArgs: MainScreenArgs(mainScreenIndex: 0), ), ); } void main() { testSetupLocator(); setUp(() { registerServices(); locator<SizeConfig>().test(); locator<GraphqlConfig>().test(); }); group('Exit Button', () { testWidgets("Tapping Tests for Exit", (tester) async { await tester.pumpWidget(createHomePageScreen()); await tester.pumpAndSettle(); tester.binding.window.physicalSizeTestValue = const Size(4000, 4000); MainScreenViewModel.scaffoldKey.currentState?.openDrawer(); await tester.pumpAndSettle(); final leaveOrg = find.byKey(MainScreenViewModel.keyDrawerLeaveCurrentOrg); await tester.tap(leaveOrg); await tester.pumpAndSettle(); final dialogPopUP = verify( (locator<NavigationService>() as MockNavigationService) .pushDialog(captureAny),) .captured; expect(dialogPopUP[0], isA<CustomAlertDialog>()); // calling success() to have complete code coverage. dialogPopUP[0].success(); }); }); group('Custom Drawer Test', () { testWidgets("Widget Testing", (tester) async { // pumping the Widget await tester.pumpWidget(createHomePageScreen()); await tester.pumpAndSettle(); // Opening the Drawer so that it can be loaded in the widget tree and built() is called await tester.dragFrom( tester.getTopLeft(find.byType(MaterialApp)), const Offset(300, 0)); await tester.pumpAndSettle(); // getting the Finders for Code Coverage expect(find.byKey(const ValueKey("Drawer")), findsOneWidget); expect(find.byKey(const ValueKey("Custom Drawer")), findsOneWidget); expect(find.text("Selected Organization"), findsOneWidget); expect(find.text("Switch Organization"), findsOneWidget); final listOfOrgs = find.byKey(const ValueKey("Switching Org")); expect(listOfOrgs, findsOneWidget); expect(find.byKey(MainScreenViewModel.keyDrawerCurOrg), findsOneWidget); expect(find.byKey(MainScreenViewModel.keyDrawerSwitchableOrg), findsOneWidget); expect(find.byType(UserAccountsDrawerHeader), findsOneWidget); expect(find.text("Join new Organization"), findsOneWidget); expect(find.text("Leave Current Organization"), findsOneWidget); final fromPalisadoes = find.byKey(const ValueKey("From Palisadoes")); expect(fromPalisadoes, findsOneWidget); }); testWidgets("Tapping Tests for Org", (tester) async { await tester.pumpWidget(createHomePageScreen()); await tester.pumpAndSettle(); // Opening the Drawer so that it can be loaded in the widget tree and built() is called await tester.dragFrom( tester.getTopLeft(find.byType(MaterialApp)), const Offset(300, 0)); await tester.pumpAndSettle(); final orgs = find.byKey(const ValueKey("Org")); // Atleast One Org should be there // ignore: invalid_use_of_protected_member expect(orgs.allCandidates.isEmpty, false); await tester.tap(orgs.first); // Was not required but done for code Coverage // Sized final sizedbox = find.byKey(const ValueKey("Sized Box Drawer")); // ignore: invalid_use_of_protected_member expect(sizedbox.allCandidates.isEmpty, false); }); testWidgets("Tapping Tests for Join", (tester) async { await tester.pumpWidget(createHomePageScreen()); await tester.pumpAndSettle(); tester.binding.window.physicalSizeTestValue = const Size(800, 4000); MainScreenViewModel.scaffoldKey.currentState?.openDrawer(); await tester.pumpAndSettle(); final joinOrg = find.byKey(MainScreenViewModel.keyDrawerJoinOrg); await tester.tap(joinOrg); // await tester.pumpAndSettle(); }); }); tearDown(() { unregisterServices(); }); }
测试过程中出现了多个异常,例如open hive Box异常。我并未在当前测试代码中直接使用Hive,想请教问题出在哪里。
解决方法
- 追踪Hive的间接调用:你测试的
MainScreen、MainScreenViewModel,或者registerServices()中注册的服务(比如GraphqlConfig、存储类等),大概率在初始化或运行时间接调用了Hive。比如ViewModel可能从Hive读取用户/组织缓存,或者服务类用Hive存储配置。 - Mock所有依赖Hive的组件:在测试的
setUp阶段,把所有会触发Hive操作的类替换为Mock实现。比如如果有封装Hive的StorageService,要注册MockStorageService代替真实类,并且通过when/thenReturn避免实际Hive调用。 - 初始化测试版Hive(若必须使用):如果某些逻辑必须依赖Hive,可以在测试前初始化Hive的测试环境:
setUp(() async { await Hive.initFlutter('test_hive_dir'); await Hive.openBox('test_box'); // 其他setup逻辑 }); tearDown(() async { await Hive.deleteFromDisk(); await Hive.close(); }); - 检查服务注册逻辑:查看
testSetupLocator()和registerServices()的实现,确保所有依赖Hive的服务都被Mock,而非使用真实实例。比如GraphqlConfig如果内部用了Hive存储token,就要Mock它对应的方法。 - Mock ViewModel的Hive相关逻辑:如果
MainScreenViewModel在初始化时读取Hive数据,需要在测试前Mock这些方法,比如:when(mockMainScreenViewModel.loadOrgData()).thenAnswer((_) async => []);
内容的提问来源于stack exchange,提问作者Indresh Goswami
相关产品推荐
相关产品推荐

