【发布时间】:2022-06-11 00:27:33
【问题描述】:
这是我第一个使用 Dart/Flutter/Riverpod 的应用程序,欢迎对代码提出任何建议或意见。
我使用Hive 作为嵌入式数据库,因此提供者状态的初始值是异步加载的,并使用riverpod 的AsyncValue 来包装它。
以下代码有效,但我对测试方法有一些疑问,所以我想确认一下我是否按照应该使用的那样使用 Riverpod 库。
这是我的提供者及其部门(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));
});
以下是测试用例,我在嘲笑 Hive 盒子提供者 (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);
// Next line waits until we have a value for preferences attribute
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 的 official docs 和与 AsyncValues 相关的 GitHub issue 作为参考
好吧,我发现了一些问题来验证是否使用正确的值调用了侦听器,我使用TypeMatcher 只是为了验证状态实例是否具有正确的类型,然后我检查(“手动”)的值如果它是预期的,则被包装对象的属性。有没有更好的方法来实现这一点?
最后,我没有找到太多以StateNotifier 和AsyncValue 作为状态类型的示例,是否有更好的方法来实现使用延迟数据初始化的提供程序?
【问题讨论】:
标签: unit-testing dart asynchronous riverpod dart-test