Why Choose Riverpod? - Testing

Search for a command to run...

No comments yet. Be the first to comment.
This is a series based off the topic I covered at the 2023 Devfest in Cape Town on 23 November.
This post is based off a presentation I did at Devfest 2023 in Cape Town on 23 Nov 2023. VIDEO | PRESENTATION What is Riverpod As described by the creator Pemi Rousselet, Riverpod is a Reactive Caching and Data-binding framework, sow while it does m...
The Problem Wasn’t Triage Most AI workflows in software engineering still keep the human directly in the middle of triage. The AI might help write code. It might explain a stack trace. It might summar

There’s been a steady undercurrent of people switching tools lately. Copilot to Claude. Claude to something else. Codex quietly entering more workflows. A lot of confidence in different directions, of

It’s been said to me that at times my responses can come across as a “no” first-unintentionally, of course, but real all the same. Not long ago, someone on my team shared an idea they were exploring. I began with a caution, meaning to help them avoid...

The opening sentence of any message often carries more weight than everything that follows. I’ve learned this the hard way. Recently, a report shared something he wanted to pursue. I was supportive — I really was — but my first response came as a cau...

One of the things I’ve always been comfortable with is admitting when I don’t know something. To me, it’s one of the real distinctions between the more and less experienced engineers — and honestly, people in general. When you’re earlier in your care...

This post is based off a presentation I did at Devfest 2023 in Cape Town on 23 Nov 2023. VIDEO | PRESENTATION
Previously we covered the ProviderScope, the secret sauce as I like to call it, finally we are going to take a look at just how much everything simplifies our lives when it comes to testing.
Testing is probably one of my favourite reasons to use Riverpod, just because of how simple it makes it.
Widget testing is where we bring back our ProviderScope
extension PumpApp on WidgetTester {
Future<void> pumpApp(
Widget widget, [
List<Override> overrides = const [],
]) async {
return pumpWidget(
ProviderScope(
overrides: overrides,
child: MaterialApp(
home: Scaffold(
body: widget,
),
),
),
);
}
}
Above is an extension I put together to make my life easier when it comes to testing, I make use of pumpApp instead of pumpWidget as it extends on pubpWidget but also allows me to easily wrap some boilerplate around my tests.
In the above example, I am wrapping every widget that gets tested with ProviderScope and also add support for optionally passing in an array of overrides to said ProviderScope.
An extension like this can also easily include things like materialApp, Scaffold and even navigation observers.
late MockConnectivityProvider mockConnectivityProvider;
setUp(() {
mockConnectivityProvider = MockConnectivityProvider();
when(() => mockConnectivityProvider.isOnline).thenReturn(true);
});
testWidgets('should show OnlineIndicator', (tester) async {
await tester.pumpApp(
widget,
[
connectivityProvider.overrideWith((_) => mockConnectivityProvider),
],
);
expect(OnlineIndicator, findsOneWidget);
});
Here we have a simple examples where we testing the OnlineIndicator widget which replies on the connectivityProvider, which is a network listener to verify that the device is both connected and is able to access the Internet.
This connectivityProvider as you may have guess is also a Riverpod provider, and as you can see in the setUp function we are mocking the response from that providers isOnline method to true, aka the devise in online.
With the pumpApp we are overriding the provider with our mocked one, and then in the test we simply verify that the OnlineIndicator does in fact renders.
When it comes to mocking the dat from any Riverpod StateNotifier, it is about as simple:
await tester.pumpApp(
widget,
[
createTripProvider.overrideWith((_) => mockCreateTripNotifier
..state = CreateTripState.initial().copyWith(jobs: mockJobs)),
],
);
Much like above we would have setup a MockTripNotifierClass, which would look slightly different to a normal mock:
class MockCreateTripNotifier extends StateNotifier<CreateTripState>
with Mock
implements CreateTripNotifier {
MockCreateTripNotifier() : super(CreateTripState());
}
Then e simply access the state directly within the override and assign it an updated value.
Another reason I love Riverpod for testing is when it comes to external packages and their ability to allow them to be very easily mocked.
One that is extensively used throughout our application is Firestore, to normally be able to mack this it would need to be an argument on the class or function whereby the instance is passed into.
Riverpod makes that much simpler. which is why within our application we have firestoreProvider which gets overridden with FirebaseFirestore.instance within the ProviderScope during the applications startup.
As you can by now tell, this means that Firestore can be directly accessed anywhere we are making use of Riverpod.
//main
firestoreProvider.overrideWith((_) => FirebaseFirestore.instance),
//Mock
late MockDocumentReference mockDocumentReference;
mockDocumentReference = MockDocumentReference();
overrides: [
firestoreProvider.overrideWith((_) => mockFirestore),
]
when(() => mockFirestore.doc('drivers/driver_id'))
.thenReturn(mockDocumentReference);
when(() => mockDocumentReference.update(payload))
.thenAnswer((_) async => Future.value());
Now when looking from a testing perspective and considering the other examples with the excerpts in the code above you can see how much simpler Riverpod makes mocking an external packages, especially ones like the Firebase packages that are exposing singletons.
I hope you found this interesting, and if you have any questions, comments, or improvements, feel free to drop a comment. Enjoy your development journey :D
Thanks for reading.