⏲️
RiverpodのFutureProviderの実装からテスト
FlutterでRiverpodのFutureProviderを使った実装からウィジェットテストまでの方法をまとめておきます。
前提
次のパッケージがインストールされていることとします。
pubspec.yaml
dependencies:
riverpod: ^2.6.1
riverpod_annotation: ^2.6.1
hooks_riverpod: ^2.6.1
dev_dependencies:
riverpod_generator: ^2.6.3
実装
今回は3秒後に文字列が表示されるウィジェットを考えます。
Future Provider
3秒後に文字列を返すFurureProviderを作ります。
riverpod_annotationを使うと、@riverpod
というアノテーションを付けるだけで簡単にProviderを定義できます。
sample_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod/riverpod.dart';
part 'sample_provider.g.dart';
FutureOr<String> sampleState(Ref ref) async {
await Future.delayed(const Duration(seconds: 3)); // 3秒待つ
return 'Hello, World!';
}
ウィジェット
ウィジェットでは上記のProviderを監視して、ローディング -> 文字列表示を行うようにします。
sample_text.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'sample_provider.dart';
class SampleText extends ConsumerWidget {
const SampleText({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final result = ref.watch(sampleStateProvider); // 監視
// この書き方はswitch式と呼ばれるもので、Riverpod ver.3.0以降で推奨されています
switch (result) {
case AsyncData(:final value):
return Text(value);
case AsyncError(:final error):
debugPrint('error: $error');
return const Text('エラー');
case _:
return const CircularProgressIndicator();
}
}
}
ちなみに、main.dart
は次のような想定です。
main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'sample_text.dart';
void main() {
runApp(const ProviderScope(child: MainApp()));
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: SampleText(),
),
),
);
}
}
テスト
ウィジェットテストではProviderをモックして特定の文字列をすぐに返すようにします。
ProviderScope
のoverrides
にProvider
のモックを設定することができます。
sample_text.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../lib/sample_provider.dart';
import '../lib/sample_text.dart';
// 正常表示のウィジェット
Widget createSuccessWidget() {
return ProviderScope(
overrides: [
// Providerをモック(3秒待たずに、Stringを返す)
sampleStateProvider.overrideWith(
(_) => '完了',
),
],
child: const MaterialApp(
home: SampleText(),
));
}
// エラー表示のウィジェット
Widget createErrorWidget() {
return ProviderScope(
overrides: [
// Providerをモック(エラー発生)
sampleStateProvider.overrideWith(
(_) {
throw Exception('error');
},
),
],
child: const MaterialApp(
home: SampleText(),
));
}
void main() {
testWidgets('正常', (WidgetTester tester) async {
await tester.pumpWidget(createSuccessWidget());
await tester.pumpAndSettle();
expect(find.text('完了'), findsOneWidget);
});
testWidgets('エラー', (WidgetTester tester) async {
await tester.pumpWidget(createErrorWidget());
await tester.pumpAndSettle();
expect(find.text('エラー'), findsOneWidget);
});
}
まとめ
Future Proverを用いたウィジェットの実装からテストまでの手順をまとめました。@riverpod
を使うと簡単にProviderを定義できます。ウィジェットで用いる際はswitch式
を使うことが推奨されています。
テストではProviderScope
のoverrides
にProvider
を指定するとモックできます。
Discussion