Riverpod1.0では、StateNotifierのMockができない?
RiverpodでWidget Testをする場合、ViewModelに該当するクラスをMockして、外部リソースにアクセスするようなロジックが含まれている関数をMockしてダミーを返したりすると思います。
そのノリで、StateNotifierProvider
をmocktail
でMockクラスを作ったところ、ProviderException
が発生し、StateNotifierProvider#addListenerでtype 'Null' is not a subtype of type '() => void'とい例外が出ます。
voidの関数が入るべき所にNull入れるなよという内容のようですが、addListener内部でネストされた関数で使われえていること、state
がprotectedなので、そもそも?みたいな話もあります。
//これ
class MockStateNotifier extends Mock implements LocaleStateNotifier {}
void main() {
final mockStateNotifier = MockStateNotifier();
testWidgets('Test', (WidgetTester tester) async {
await tester.pumpWidget(return ProviderScope(
overrides: [localeStateProvider.overrideWithValue(mockStateNotifier)],
child: const MaterialApp(
home: MyApp(),
),
);
);
});
}
結論からすれば、Mockできないなら、StateNotifierProviderを使う場合、StateNotifierにはstateを更新する以外のロジックを持ってはいけない という所かな。Mockで関数上書きできないから。
なので、こういう感じのコードを書いている。getHogeHoge
は外部データ等を取得するクラスをProviderで管理。取得するクラスの中で、ref.watch
でStateNotifierを取得して入力値を取得。こうすると、画面の依存関係が全部RiverPodで解決できるので、どうせRiverPod使ってるならそういうアーキで良くないって思ってる。
provider.getHogeHoge().then((value) => state.setHogeHoge(value));
RiverPodのIssueが上がってて、上げた人が自己解決してるけど、微妙に解釈が違う。Mocktail
でのMockに失敗したって話が、Mockすら使ってないじゃんw コンストラクタにDummyState入れたら誰でもできるよ〜。
Widgetテストを書くと、疎結合とは何かを再考するいい機会になりますね。
Discussion