website/docs/how_to/testing.mdx
import { AutoSnippet } from "/src/components/CodeSnippet"; import unitTest from "!!raw-loader!./testing/unit_test.dart"; import widgetTest from "!!raw-loader!./testing/widget_test.dart"; import fullWidgetTest from "!!raw-loader!./testing/full_widget_test.dart"; import testerContainer from "!!raw-loader!./testing/tester_container.dart"; import providerToMock from "./testing/provider_to_mock"; import mockProvider from "!!raw-loader!./testing/mock_provider.dart"; import autoDisposeListen from "!!raw-loader!./testing/auto_dispose_listen.dart"; import listenProvider from "!!raw-loader!./testing/listen_provider.dart"; import awaitFuture from "!!raw-loader!./testing/await_future.dart"; import notifierMock from "./testing/notifier_mock"; import notifierUsage from "!!raw-loader!./testing/notifier_usage.dart";
A core part of the Riverpod API is the ability to test your providers in isolation.
For a proper test suite, there are a few challenges to overcome:
Fortunately, Riverpod makes it easy to achieve all of these goals.
When defining a test with Riverpod, there are two main scenarios:
Unit tests are defined using the test function from package:test.
The main difference with any other test is that we will want to create
a ProviderContainer object. This object will enable our test to interact
with providers.
A typical test using ProviderContainer will look like:
Now that we have a ProviderContainer, we can use it to read providers using:
container.read, to read the current value of a provider.container.listen, to listen to a provider and be notified of changes.:::caution
Be careful when using container.read when providers are automatically disposed.
If your provider is not listened to, chances are that its state will get destroyed
in the middle of your test.
In that case, consider using container.listen.
Its return value enables reading the current value of provider anyway,
but will also ensure that the provider is not disposed in the middle of your test:
Widget tests are defined using the testWidgets function from package:flutter_test.
In this case, the main difference with usual Widget tests is that we must add
a ProviderScope widget at the root of tester.pumpWidget:
This is similar to what we do when we enable Riverpod in our Flutter app.
Then, we can use tester to interact with our widget.
Alternatively if you want to interact with providers, you can obtain
a ProviderContainer.
One can be obtained using tester.container().
By using tester, we can therefore write the following:
We can then use it to read providers. Here's a full example:
<AutoSnippet raw={fullWidgetTest} />So far, we've seen how to set up a test and basic interactions with providers. However, in some cases, we may want to mock a provider.
The cool part: All providers can be mocked by default, without any additional setup.
This is possible by specifying the overrides parameter on either
ProviderScope or ProviderContainer.
Consider the following provider:
<AutoSnippet {...providerToMock} />
We can mock it using:
<AutoSnippet raw={mockProvider} />Since we obtained a ProviderContainer in our tests, it is possible to
use it to "listen" to a provider:
You can then combine this with packages such as mockito
or mocktail to use their verify API.
Or more simply, you can add all changes in a list and assert on it.
In Riverpod, it is very common for providers to return a Future/Stream.
In that case, chances are that our tests need to await for that asynchronous operation
to be completed.
One way to do so is to read the .future of a provider:
It is generally discouraged to mock Notifiers. This is because Notifiers cannot be instantiated on their own, and only work when used as part of a Provider.
Instead, you should likely introduce a level of abstraction in the logic of your Notifier, such that you can mock that abstraction. For instance, rather than mocking a Notifier, you could mock a "repository" that the Notifier uses to fetch data from.
If you insist on mocking a Notifier, there is a special consideration to create such a mock: Your mock must subclass the original Notifier base class: You cannot "implement" Notifier, as this would break the interface.
As such, when mocking a Notifier, instead of writing the following mockito code:
class MyNotifierMock with Mock implements MyNotifier {}
You should instead write:
<AutoSnippet {...notifierMock} />
:::info
If using code-generation, for the above to work, your mock will have to
be placed in the same file as the Notifier you are mocking.
Otherwise you would not have access to the _$MyNotifier class.
Then, to use your notifier you could do:
<AutoSnippet raw={notifierUsage} />