Dart中Riverpod的StateNotifierProvider与AsyncValue测试及实现咨询
Dart/Flutter 基于Riverpod+Hive的开发问题
这是我首次开发基于Dart/Flutter/Riverpod的应用,欢迎对代码提出任何建议或意见。
我采用Hive作为嵌入式数据库,因此provider状态的初始值为异步加载,使用Riverpod提供的AsyncValue对该异步状态进行包装。
下述代码可正常运行,但我对测试方案存在疑问,希望确认当前对Riverpod库的使用方式是否符合规范。
Provider及依赖实现代码
Preferences是用于存储应用通用配置数据的HiveObject,相关实现代码如下:
final hiveProvider = FutureProvider<HiveInterface>((ref) async { return await App.setUp(); }); final prefBoxProvider = FutureProvider<Box<Preferences>>((ref) async { final HiveInterface hive = await ref.read(hiveProvider.future); return hive.openBox<Preferences>("preferences"); }); class PreferencesNotifier extends StateNotifier<AsyncValue<Preferences>> { late Box<Preferences> prefBox; PreferencesNotifier(Future<Box<Preferences>> prefBoxFuture): super(const AsyncValue.loading()) { prefBoxFuture.then((value) { prefBox = value; _loadCurrentPreferences(); }); } void _loadCurrentPreferences() { Preferences pref = prefBox.get(0) ?? Preferences(); state = AsyncValue.data(pref); } Future<void> save(Preferences prefs) async { await prefBox.put(0, prefs); state = AsyncValue.data(prefs); } Preferences? get preferences { return state.when(data: (value) => value, error: (_, __) => null, loading: () => null); } } final preferencesProvider = StateNotifierProvider<PreferencesNotifier, AsyncValue<Preferences>>((ref) { return PreferencesNotifier(ref.read(prefBoxProvider.future)); });
测试用例实现
测试中我mock了Hive box provider(prefBoxProvider),代码如下:
class Listener extends Mock { void call(dynamic previous, dynamic value); } Future<Box<Preferences>> prefBoxTesting() async { final hive = await App.setUp(); Box<Preferences> box = await hive.openBox<Preferences>("testing_preferences"); await box.clear(); return box; } void main() { test('Preferences value changes', () async { final container = ProviderContainer(overrides: [ prefBoxProvider.overrideWithValue(AsyncValue.data(await prefBoxTesting())) ],); addTearDown(() { container.dispose(); Hive.deleteBoxFromDisk("testing_preferences"); }); final listener = Listener(); container.listen<AsyncValue<Preferences>>( preferencesProvider, listener, fireImmediately: true, ); verify(listener(null, const TypeMatcher<AsyncLoading>())).called(1); verifyNoMoreInteractions(listener); // 等待preferences属性加载完成 await container.read(preferencesProvider.notifier).stream.first; verify(listener(const TypeMatcher<AsyncLoading>(), const TypeMatcher<AsyncData>())).called(1); Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!); preferences.currentListName = 'Lista1'; await container.read(preferencesProvider.notifier).save(preferences); verify(listener(const TypeMatcher<AsyncData>(), const TypeMatcher<AsyncData>())).called(1); verifyNoMoreInteractions(listener); final name = container.read(preferencesProvider.notifier).preferences!.currentListName; expect(name, equals('Lista1')); }); }
我参考Riverpod官方测试文档以及AsyncValue相关GitHub issue编写了上述代码。
待解决的两个问题
- 验证listener被传入正确参数值时存在障碍:当前仅使用
TypeMatcher验证状态实例的类型是否正确,再手动检查包装对象的属性值是否符合预期,想了解是否存在更优的实现方式。 - 目前可参考的将
StateNotifier与AsyncValue结合作为状态类型的示例较少,想了解针对依赖延迟加载数据初始化的provider,是否存在更优的实现方案。
内容的提问来源于stack exchange,提问作者Roberto
相关产品推荐
相关产品推荐

