🐕‍🦺

Riverpod1.0では、StateNotifierのMockができない?

2021/11/22に公開

RiverpodでWidget Testをする場合、ViewModelに該当するクラスをMockして、外部リソースにアクセスするようなロジックが含まれている関数をMockしてダミーを返したりすると思います。

そのノリで、StateNotifierProvidermocktailで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入れたら誰でもできるよ〜。

https://github.com/rrousselGit/river_pod/issues/934

Widgetテストを書くと、疎結合とは何かを再考するいい機会になりますね。

Discussion