Flutter用Mockito写弹窗测试用例遇空类型错误及全覆盖求助
Flutter Mockito测试空类型错误与全量代码覆盖解决方案
问题概述
测试handleCaregiverRouteforWeb方法时触发type 'Null' is not a subtype of type 'Widget'错误,已确认context和localizer不为空但问题仍存在;同时需要覆盖该方法最后3行的导航与埋点逻辑,实现全方法代码覆盖。
错误原因分析
从报错栈可以定位到,错误起源于getUserProfileAppOnboardingCompleted方法调用时,依赖MediaQuery.of(context)获取屏幕信息,但MockBuildContext默认没有关联任何Widget树,导致MediaQuery.of返回null,进而引发空类型转换错误。另外测试代码存在逻辑矛盾:设置featureFlagService.hasAccess(FeatureFlag.BrazeIAMEvent)返回false,却又验证mockBehaviorTrackerService.track(any)被调用,这会导致测试失败。
解决方案
一、修复空类型错误
- Mock MediaQuery依赖
因为getUserProfileAppOnboardingCompleted内部通过MediaQuery.of获取屏幕尺寸,需要给MockBuildContext模拟MediaQuery环境,避免真实Widget依赖:when(MediaQuery.of(mockBuildContext)).thenReturn(MediaQueryData(size: Size(1920, 1080))); - 修正测试逻辑矛盾
当设置featureFlagService.hasAccess返回false时,移除verify(mockBehaviorTrackerService.track(any));;若要测试埋点逻辑,单独编写FeatureFlag为true的测试用例。
二、实现全量代码覆盖
需要编写3个测试用例覆盖所有分支场景:
- 场景1:用户是Caregiver + FeatureFlag关闭(覆盖弹窗、导航逻辑)
- 场景2:用户是Caregiver + FeatureFlag开启(覆盖弹窗、导航、埋点逻辑)
- 场景3:用户非Caregiver(覆盖不弹窗、导航逻辑)
完整测试代码示例
测试用例1:Caregiver用户 + FeatureFlag关闭
test('handleCaregiverRouteforWeb updates status, shows dialog and navigates when caregiver (FeatureFlag off)', () async { final mockBuildContext = MockBuildContext(); final testLocalizer = TestLocalizer(); final viewModel = DeviceVerificationScreenViewModel( testLocalizer, context: mockBuildContext, memberInformation: memberInformationMinusPhone, ); // Mock MediaQuery避免空错误 when(MediaQuery.of(mockBuildContext)).thenReturn(MediaQueryData(size: Size(1920, 1080))); when(mockUserService.currentUser).thenReturn(caregiverMock); when(mockNavigationService.goToRoute(HomeRoute)).thenAnswer((_) async => null); when(mockFeatureFlagService.hasAccess(FeatureFlag.BrazeIAMEvent)).thenReturn(false); // Mock showDialogPopup(用anyNamed匹配builder参数,避免精确匹配Widget实例的问题) when(showDialogPopup( context: mockBuildContext, barrierDismissible: false, builder: anyNamed('builder'), )).thenAnswer((_) async {}); await viewModel.handleCaregiverRouteforWeb(); verify(mockUserService.updateWebUserLoggedInStatus()).called(1); verify(showDialogPopup( context: mockBuildContext, barrierDismissible: false, builder: anyNamed('builder'), )).called(1); verify(mockNavigationService.goToRoute(HomeRoute)).called(1); verifyNever(mockBehaviorTrackerService.track(any)); });
测试用例2:Caregiver用户 + FeatureFlag开启
test('handleCaregiverRouteforWeb tracks event when FeatureFlag is on', () async { final mockBuildContext = MockBuildContext(); final testLocalizer = TestLocalizer(); final viewModel = DeviceVerificationScreenViewModel( testLocalizer, context: mockBuildContext, memberInformation: memberInformationMinusPhone, ); when(MediaQuery.of(mockBuildContext)).thenReturn(MediaQueryData(size: Size(1920, 1080))); when(mockUserService.currentUser).thenReturn(caregiverMock); when(mockNavigationService.goToRoute(HomeRoute)).thenAnswer((_) async => null); when(mockFeatureFlagService.hasAccess(FeatureFlag.BrazeIAMEvent)).thenReturn(true); when(showDialogPopup( context: mockBuildContext, barrierDismissible: false, builder: anyNamed('builder'), )).thenAnswer((_) async {}); await viewModel.handleCaregiverRouteforWeb(); verify(mockBehaviorTrackerService.track(getUserProfileAppOnboardingCompleted(mockBuildContext))).called(1); });
测试用例3:非Caregiver用户
test('handleCaregiverRouteforWeb skips dialog when not caregiver', () async { final mockBuildContext = MockBuildContext(); final testLocalizer = TestLocalizer(); final viewModel = DeviceVerificationScreenViewModel( testLocalizer, context: mockBuildContext, memberInformation: memberInformationMinusPhone, ); when(MediaQuery.of(mockBuildContext)).thenReturn(MediaQueryData(size: Size(1920, 1080))); when(mockUserService.currentUser).thenReturn(nonCaregiverMock); // 传入非护理人员用户实例 when(mockNavigationService.goToRoute(HomeRoute)).thenAnswer((_) async => null); when(mockFeatureFlagService.hasAccess(FeatureFlag.BrazeIAMEvent)).thenReturn(false); await viewModel.handleCaregiverRouteforWeb(); verifyNever(showDialogPopup( context: mockBuildContext, barrierDismissible: false, builder: anyNamed('builder'), )); verify(mockNavigationService.goToRoute(HomeRoute)).called(1); });
内容的提问来源于stack exchange,提问作者Nithyashree B L
相关产品推荐
相关产品推荐

