How do you test code that uses Riverpod providers?
Short Answer
Wrap the widget or logic under test in a ProviderScope with overrides — swapping real providers for test doubles via overrideWith or overrideWithValue — so tests never hit real network/database calls.
Riverpod's ProviderScope accepts an overrides list, letting you replace any provider with a fake implementation just for that scope. For a repository provider: overrides: [userRepositoryProvider.overrideWithValue(mockRepo)]. For an AsyncNotifierProvider with its own async logic, overrideWith lets you substitute the whole provider's implementation.
This makes it possible to test a widget's behavior across loading/data/error states deterministically, by overriding a provider to immediately return each state, rather than depending on real timing from an actual async call.
Code Example
testWidgets('shows user name when loaded', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
userRepositoryProvider.overrideWithValue(MockUserRepository()),
],
child: const MaterialApp(home: ProfileScreen()),
),
);
await tester.pumpAndSettle();
expect(find.text('Ana'), findsOneWidget);
});Common Mistakes
- ×Using overrideWithValue for a provider whose behavior (not just its value) needs to change between tests — overrideWith gives a full replacement implementation.
- ×Forgetting that a fresh ProviderScope is needed per test to avoid state leaking between tests.