--- title: Testing your providers version: 4 --- 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: - Tests should not share state. This means that new tests should not be affected by the previous tests. - Tests should give us the ability to mock certain functionalities to achieve the desired state. - The test environment should be as close as possible to the real environment. Fortunately, Riverpod makes it easy to achieve all of these goals. ## Setting up a test When defining a test with Riverpod, there are two main scenarios: - Unit tests, usually with no Flutter dependency. This can be useful for testing the behavior of a provider in isolation. - Widget tests, usually with a Flutter dependency. This can be useful for testing the behavior of a widget that uses a provider. ### Unit tests Unit tests are defined using the `test` function from [package:test](https://pub.dev/packages/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 Widget tests are defined using the `testWidgets` function from [package:flutter_test](https://pub.dev/packages/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: ## Mocking providers 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: We can mock it using: ## Spying on changes in a provider 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](https://pub.dev/packages/mockito) or [mocktail](https://pub.dev/packages/mocktail) to use their `verify` API. Or more simply, you can add all changes in a list and assert on it. ## Awaiting asynchronous providers 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: ## Mocking Notifiers 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: ```dart class MyNotifierMock with Mock implements MyNotifier {} ``` You should instead write: :::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: