使用Mockito模拟StateNotifierProvider(Riverpod)遇初始化断言错误
解决Riverpod StateNotifierProvider测试中未初始化的断言错误
你在测试按钮行组件时遇到断言错误:组件的按钮启用/禁用状态由控制器类的标志决定,手动测试正常,但自动化测试中,用Mockito创建MockUserSettingsController并覆盖StateNotifierProvider后,Widget的build方法执行ref.watch(userSettingsControllerProvider)时抛出以下错误:
Exception has occurred. _AssertionError ('package:riverpod/src/framework/element.dart': Failed assertion: line 439 pos 9: 'getState() != null': Bad state, the provider did not initialize. Did "create" forget to set the state?)
你尝试过Stack Overflow上的两种方案,但因StateNotifier的state属性使用方式变更,且需要重写控制器的hasValueChanged()方法,这些方案无法适用。
相关代码
待测试Widget代码
class UserSettingsButtonRow extends ConsumerWidget { const UserSettingsButtonRow({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { ref.watch(userSettingsControllerProvider); UserSettingsController controller = ref.read(userSettingsControllerProvider.notifier); ... }
测试代码
@GenerateNiceMocks([MockSpec<UserSettingsController>()]) void main() { testWidgets( 'UserSettingsButtonRow - buttons enabled', (tester) async { // check if two buttons are enabled UserSettingsController settingsController = MockUserSettingsController(); when(settingsController.hasValueChanged()).thenAnswer((_) => true); await tester.runAsync( () async { await tester.pumpWidget( ProviderScope( overrides: [ userSettingsControllerProvider .overrideWith((ref) => settingsController), ], child: const MaterialApp( home: Material( child: UserSettingsButtonRow(), ), ), ), ); }, ); await tester.pumpAndSettle(); expect(find.byType(ElevatedButton), findsNWidgets(2)); List<ElevatedButton> buttons = find .byType(ElevatedButton) .evaluate() .map((e) => e.widget as ElevatedButton) .toList(); for (ElevatedButton button in buttons) { expect(button.enabled, isTrue); } }, ); <.......> }
解决方案
错误核心是Mock控制器未初始化StateNotifier的状态:当ref.watch调用时,Riverpod会尝试获取Provider的状态,但Mock对象的状态为null,触发了断言检查。
1. 给Mock控制器初始化状态
因为UserSettingsController继承自StateNotifier,必须确保它的state不为null。根据你的控制器状态类型,创建初始状态实例并通过Mockito模拟返回:
// 假设你的控制器状态类型是UserSettings,根据实际类定义初始化 final initialState = UserSettings(); UserSettingsController settingsController = MockUserSettingsController(); // 模拟state getter返回初始状态 when(settingsController.state).thenReturn(initialState); when(settingsController.hasValueChanged()).thenAnswer((_) => true);
2. 调整Provider覆盖方式(可选)
如果使用Riverpod 2.x,也可以用overrideWithProvider创建临时Provider返回Mock,确保状态初始化逻辑被正确识别:
overrides: [ userSettingsControllerProvider.overrideWithProvider( StateNotifierProvider((ref) => settingsController), ), ],
3. 验证Provider定义
检查你的userSettingsControllerProvider定义,确保状态类型不为空。如果业务允许状态为null,需显式声明可为空类型:
final userSettingsControllerProvider = StateNotifierProvider<UserSettingsController, UserSettings?>(...);
但更推荐确保状态始终有初始值,避免此类断言错误。
内容的提问来源于stack exchange,提问作者Vera Spöttl-Zeisberg
相关产品推荐
相关产品推荐

