如何基于Stacked Architecture进行Widget测试?
Stacked架构下Widget测试依赖注入问题解决方法
问题场景
采用Stacked架构+控制反转的Flutter项目中,单元测试运行正常,但执行Widget/集成测试时启动阶段报错,提示AuthService或NavigationService未在GetIt中注册。
测试代码示例:
import 'package:flutter_test/flutter_test.dart'; import 'package:my_project/ui/start_up/startup_viewmodel.dart'; void main() { testWidgets( "Simple widget test", (WidgetTester tester) async { await tester.pumpWidget(StartUpView()); }, ); }
报错信息:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ The following assertion was thrown building StartUpView: Object/factory with type AuthService is not registered inside GetIt. (Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance; Did you forget to register it?) 'package:get_it/get_it_impl.dart': Failed assertion: line 372 pos 7: 'instanceFactory != null' The relevant error-causing widget was: StartUpView StartUpView:file:path/my_project/lib/app/app.router.dart:87:37
问题根源:StartUpView对应的ViewModel通过locator<T>获取服务,但测试环境未初始化Stacked的依赖注入容器,也未注册所需服务。
解决方法
1. 手动初始化Locator并注册依赖
在测试用例执行前,初始化Stacked的locator并注册所有ViewModel依赖的服务,推荐使用Mock类隔离测试逻辑:
import 'package:flutter_test/flutter_test.dart'; import 'package:my_project/ui/start_up/startup_view.dart'; import 'package:my_project/services/auth_service.dart'; import 'package:my_project/services/navigation_service.dart'; import 'package:stacked/stacked.dart'; import 'package:mockito/mockito.dart'; // 生成Mock服务类(需依赖mockito包) class MockAuthService extends Mock implements AuthService {} class MockNavigationService extends Mock implements NavigationService {} void main() { setUp(() { // 重置locator,确保每个测试用例环境干净 locator.reset(); // 注册Mock服务 locator.registerLazySingleton<AuthService>(() => MockAuthService()); locator.registerLazySingleton<NavigationService>(() => MockNavigationService()); }); testWidgets("Simple widget test", (WidgetTester tester) async { await tester.pumpWidget(const StartUpView()); // 可添加断言验证页面状态,例如: // expect(find.text("Welcome"), findsOneWidget); }); }
2. 使用StackedTest包简化测试流程
Stacked官方提供stacked_test包,可自动处理locator的初始化与清理,简化测试代码:
import 'package:flutter_test/flutter_test.dart'; import 'package:my_project/ui/start_up/startup_view.dart'; import 'package:my_project/services/auth_service.dart'; import 'package:my_project/services/navigation_service.dart'; import 'package:stacked_test/stacked_test.dart'; import 'package:mockito/mockito.dart'; class MockAuthService extends Mock implements AuthService {} class MockNavigationService extends Mock implements NavigationService {} void main() { testWidgets("Simple widget test", (WidgetTester tester) async { // 一键初始化测试环境并注册依赖 await setupTestWidget( tester: tester, widget: const StartUpView(), dependencies: [ LazySingletonMock<AuthService>(MockAuthService()), LazySingletonMock<NavigationService>(MockNavigationService()), ], ); // 执行测试断言 expect(find.byType(StartUpView), findsOneWidget); }); }
3. 集成测试适配
集成测试的处理逻辑与Widget测试一致,只需在测试入口处初始化locator并注册所有必要依赖,再启动App:
import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:my_project/main.dart'; import 'package:my_project/services/auth_service.dart'; import 'package:my_project/services/navigation_service.dart'; import 'package:stacked/stacked.dart'; import 'package:mockito/mockito.dart'; class MockAuthService extends Mock implements AuthService {} class MockNavigationService extends Mock implements NavigationService {} void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); setUp(() { locator.reset(); locator.registerLazySingleton<AuthService>(() => MockAuthService()); locator.registerLazySingleton<NavigationService>(() => MockNavigationService()); }); testWidgets("Integration test example", (WidgetTester tester) async { await tester.pumpWidget(const MyApp()); // 执行集成测试逻辑 }); }
内容的提问来源于stack exchange,提问作者Jakub F. Romański
相关产品推荐
相关产品推荐

