riverpod
riverpod copied to clipboard
How to UnitTest a FutureProvider.autoDispose()?
Describe what scenario you think is uncovered by the existing examples/articles I successfully tested my FutureProvider adapting this code from riverpod.de
test('override repositoryProvider', () async {
final container = ProviderContainer(
overrides: [
// Override the behavior of repositoryProvider to return
// FakeRepository instead of Repository.
repositoryProvider.overrideWithProvider(Provider((ref) => FakeRepository()))
// We do not have to override `todoListProvider`, it will automatically
// use the overriden repositoryProvider
],
);
// The first read if the loading state
expect(
container.read(todoListProvider),
const AsyncValue<List<Todo>>.loading(),
);
/// Wait for the request to finish
await Future<void>.value();
// Exposes the data fetched
expect(container.read(todoListProvider).data.value, [
isA<Todo>()
.having((s) => s.id, 'id', '42')
.having((s) => s.label, 'label', 'Hello world')
.having((s) => s.completed, 'completed', false),
]);
});
In my case, it looks like this:
test('contactProvider should provide Contact for id', () async {
final loading = container.read(contactProvider(42));
expect(loading, const AsyncValue<Contact>.loading());
await Future<void>.value();
final contact = container.read(contactProvider(42)).data.value;
expect(contact, isNotNull);
expect(contact.id, 42);
expect(contact.name, 'contact-name-42');
});
However, since the contact with the id ultimately comes from a db, I don't want to cache the result. When I modify my provider with autodispose, the test fails with NoSuchMethodError: The getter 'value' was called on null.
3 hours of googling and stackoverflowing and experimenting were unsuccessful.
Your "await" isn't actually waiting for the future to complete.
Since this provider is an autoDispose
, the provider was destroyed during the "await" because there are no listeners. So the second container.read
creates a new future, which wasn't awaited.
Similarly, you may want to read provider.future
as it is more adapted to "awaiting" the completion
Do:
FutureProvider provider;
final container = ProviderContainer(...);
// as opposed to "read", this will keep the provider alive until the subscription is closed
final subscription = container.listen(provider.future);
await expectLater(
subscription.read(),
completion(
isA<Contact>()
.having((c) => c.id, 'id', 42)
.having((c) => c.name, 'name', 'contact-name-42')
),
);
Thank you very much for your fast reply. As far as I am concerned this answers my question perfectly. I am unsure whether I can / should close this issue or whether you want to keep it open as a reminder to perhaps enhance the documentation.
Have a good start into the New Year :)
Done in 5eceb2b934d3ba6b5783df284be7213a131e2e7b